mirror of https://gitee.com/bigwinds/arangodb
optimizations for neighbors queries
This commit is contained in:
parent
a0b67ae7ad
commit
55fce39574
|
@ -3630,7 +3630,7 @@ void arangodb::aql::optimizeTraversalsRule(Optimizer* opt,
|
|||
auto outVariable = traversal->edgeOutVariable();
|
||||
if (outVariable != nullptr &&
|
||||
varsUsedLater.find(outVariable) == varsUsedLater.end()) {
|
||||
// traversal vertex outVariable not used later
|
||||
// traversal edge outVariable not used later
|
||||
traversal->setEdgeOutput(nullptr);
|
||||
modified = true;
|
||||
}
|
||||
|
@ -3638,7 +3638,7 @@ void arangodb::aql::optimizeTraversalsRule(Optimizer* opt,
|
|||
outVariable = traversal->pathOutVariable();
|
||||
if (outVariable != nullptr &&
|
||||
varsUsedLater.find(outVariable) == varsUsedLater.end()) {
|
||||
// traversal vertex outVariable not used later
|
||||
// traversal path outVariable not used later
|
||||
traversal->setPathOutput(nullptr);
|
||||
modified = true;
|
||||
}
|
||||
|
@ -3655,6 +3655,52 @@ void arangodb::aql::optimizeTraversalsRule(Optimizer* opt,
|
|||
n->walk(&finder);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// now check if we can use an optimized version of the neighbors search...
|
||||
if (!arangodb::ServerState::instance()->isRunningInCluster()) {
|
||||
for (auto const& n : tNodes) {
|
||||
TraversalNode* traversal = static_cast<TraversalNode*>(n);
|
||||
|
||||
if (traversal->edgeOutVariable() != nullptr ||
|
||||
traversal->pathOutVariable() != nullptr) {
|
||||
// traversal produces edges or paths
|
||||
continue;
|
||||
}
|
||||
|
||||
if (traversal->maxDepth() > 100) {
|
||||
// neighbors search is recursive... do not use recursive version if
|
||||
// depth is potentially high
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!traversal->expressions()->empty()) {
|
||||
// traversal has filter expressions
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!traversal->allDirectionsEqual()) {
|
||||
// not all directions are equal
|
||||
continue;
|
||||
}
|
||||
|
||||
TraversalOptions const* options = traversal->options();
|
||||
TRI_ASSERT(options != nullptr);
|
||||
|
||||
if (options->uniqueVertices != traverser::TraverserOptions::GLOBAL ||
|
||||
options->uniqueEdges != traverser::TraverserOptions::NONE) {
|
||||
// neighbors search is hard-coded to global vertex uniqueness
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!options->useBreadthFirst) {
|
||||
continue;
|
||||
}
|
||||
|
||||
traversal->specializeToNeighborsSearch();
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
opt->addPlan(plan, rule, modified);
|
||||
}
|
||||
|
|
|
@ -28,7 +28,10 @@
|
|||
#include "Aql/ExecutionPlan.h"
|
||||
#include "Aql/Functions.h"
|
||||
#include "Basics/ScopeGuard.h"
|
||||
#include "Basics/StringRef.h"
|
||||
#include "Cluster/ClusterTraverser.h"
|
||||
#include "Utils/OperationCursor.h"
|
||||
#include "Utils/Transaction.h"
|
||||
#include "VocBase/SingleServerTraverser.h"
|
||||
#include "V8/v8-globals.h"
|
||||
|
||||
|
@ -261,6 +264,12 @@ int TraversalBlock::initializeCursor(AqlItemBlock* items, size_t pos) {
|
|||
/// @brief read more paths
|
||||
bool TraversalBlock::morePaths(size_t hint) {
|
||||
DEBUG_BEGIN_BLOCK();
|
||||
|
||||
TraversalNode const* planNode = static_cast<TraversalNode const*>(getPlanNode());
|
||||
if (planNode->_specializedNeighborsSearch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
freeCaches();
|
||||
_posInPaths = 0;
|
||||
if (!_traverser->hasMore()) {
|
||||
|
@ -268,7 +277,7 @@ bool TraversalBlock::morePaths(size_t hint) {
|
|||
_engine->_stats.filtered += _traverser->getAndResetFilteredPaths();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (usesVertexOutput()) {
|
||||
_vertices.reserve(hint);
|
||||
}
|
||||
|
@ -315,6 +324,8 @@ bool TraversalBlock::morePaths(size_t hint) {
|
|||
/// @brief skip the next paths
|
||||
size_t TraversalBlock::skipPaths(size_t hint) {
|
||||
DEBUG_BEGIN_BLOCK();
|
||||
TRI_ASSERT(!static_cast<TraversalNode const*>(getPlanNode())->_specializedNeighborsSearch);
|
||||
|
||||
freeCaches();
|
||||
_posInPaths = 0;
|
||||
if (!_traverser->hasMore()) {
|
||||
|
@ -331,6 +342,9 @@ void TraversalBlock::initializePaths(AqlItemBlock const* items) {
|
|||
// No Initialization required.
|
||||
return;
|
||||
}
|
||||
|
||||
TraversalNode const* planNode = static_cast<TraversalNode const*>(getPlanNode());
|
||||
|
||||
if (!_useRegister) {
|
||||
if (!_usedConstant) {
|
||||
_usedConstant = true;
|
||||
|
@ -341,22 +355,36 @@ void TraversalBlock::initializePaths(AqlItemBlock const* items) {
|
|||
"Only id strings or objects with "
|
||||
"_id are allowed");
|
||||
} else {
|
||||
_traverser->setStartVertex(_vertexId);
|
||||
if (planNode->_specializedNeighborsSearch) {
|
||||
// fetch neighbor nodes
|
||||
neighbors(_vertexId);
|
||||
} else {
|
||||
_traverser->setStartVertex(_vertexId);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
AqlValue const& in = items->getValueReference(_pos, _reg);
|
||||
if (in.isObject()) {
|
||||
try {
|
||||
std::string idString = _trx->extractIdString(in.slice());
|
||||
_traverser->setStartVertex(idString);
|
||||
if (planNode->_specializedNeighborsSearch) {
|
||||
// fetch neighbor nodes
|
||||
neighbors(_trx->extractIdString(in.slice()));
|
||||
} else {
|
||||
_traverser->setStartVertex(_trx->extractIdString(in.slice()));
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
// _id or _key not present... ignore this error and fall through
|
||||
}
|
||||
} else if (in.isString()) {
|
||||
_vertexId = in.slice().copyString();
|
||||
_traverser->setStartVertex(_vertexId);
|
||||
if (planNode->_specializedNeighborsSearch) {
|
||||
// fetch neighbor nodes
|
||||
neighbors(_vertexId);
|
||||
} else {
|
||||
_traverser->setStartVertex(_vertexId);
|
||||
}
|
||||
} else {
|
||||
_engine->getQuery()->registerWarning(
|
||||
TRI_ERROR_BAD_PARAMETER, "Invalid input for traversal: Only "
|
||||
|
@ -511,3 +539,137 @@ size_t TraversalBlock::skipSome(size_t atLeast, size_t atMost) {
|
|||
return atMost;
|
||||
DEBUG_END_BLOCK();
|
||||
}
|
||||
|
||||
/// @brief optimized version of neighbors search, must properly implement this
|
||||
void TraversalBlock::neighbors(std::string const& startVertex) {
|
||||
std::unordered_set<VPackSlice, basics::VelocyPackHelper::VPackStringHash, basics::VelocyPackHelper::VPackStringEqual> visited;
|
||||
|
||||
std::vector<VPackSlice> result;
|
||||
result.reserve(1000);
|
||||
|
||||
TransactionBuilderLeaser builder(_trx);
|
||||
builder->add(VPackValue(startVertex));
|
||||
|
||||
std::vector<VPackSlice> startVertices;
|
||||
startVertices.emplace_back(builder->slice());
|
||||
visited.emplace(builder->slice());
|
||||
|
||||
TRI_edge_direction_e direction = TRI_EDGE_ANY;
|
||||
std::string collectionName;
|
||||
traverser::TraverserOptions const* options = _traverser->options();
|
||||
if (options->getCollection(0, collectionName, direction)) {
|
||||
runNeighbors(startVertices, visited, result, direction, 1);
|
||||
}
|
||||
|
||||
TRI_doc_mptr_t mptr;
|
||||
_vertices.clear();
|
||||
_vertices.reserve(result.size());
|
||||
for (auto const& it : result) {
|
||||
VPackValueLength l;
|
||||
char const* p = it.getString(l);
|
||||
StringRef ref(p, l);
|
||||
|
||||
size_t pos = ref.find('/');
|
||||
if (pos == std::string::npos) {
|
||||
// invalid id
|
||||
continue;
|
||||
}
|
||||
|
||||
int res = _trx->documentFastPathLocal(ref.substr(0, pos).toString(), ref.substr(pos + 1).toString(), &mptr);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
_vertices.emplace_back(AqlValue(mptr.vpack()));
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief worker for neighbors() function
|
||||
void TraversalBlock::runNeighbors(std::vector<VPackSlice> const& startVertices,
|
||||
std::unordered_set<VPackSlice, basics::VelocyPackHelper::VPackStringHash, basics::VelocyPackHelper::VPackStringEqual>& visited,
|
||||
std::vector<VPackSlice>& distinct,
|
||||
TRI_edge_direction_e direction,
|
||||
uint64_t depth) {
|
||||
std::vector<VPackSlice> nextDepth;
|
||||
bool initialized = false;
|
||||
TraversalNode const* node = static_cast<TraversalNode const*>(getPlanNode());
|
||||
traverser::TraverserOptions const* options = _traverser->options();
|
||||
|
||||
TransactionBuilderLeaser builder(_trx);
|
||||
|
||||
size_t const n = options->collectionCount();
|
||||
std::string collectionName;
|
||||
Transaction::IndexHandle indexHandle;
|
||||
|
||||
std::vector<TRI_doc_mptr_t*> edges;
|
||||
|
||||
for (auto const& startVertex : startVertices) {
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
builder->clear();
|
||||
|
||||
if (!options->getCollectionAndSearchValue(i, startVertex.copyString(), collectionName, indexHandle, *builder.builder())) {
|
||||
TRI_ASSERT(false);
|
||||
}
|
||||
|
||||
std::shared_ptr<OperationCursor> cursor = _trx->indexScan(collectionName,
|
||||
arangodb::Transaction::CursorType::INDEX, indexHandle,
|
||||
builder->slice(), 0, UINT64_MAX, 1000, false);
|
||||
|
||||
if (cursor->failed()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
edges.clear();
|
||||
while (cursor->hasMore()) {
|
||||
cursor->getMoreMptr(edges, 1000);
|
||||
|
||||
for (auto const& it : edges) {
|
||||
VPackSlice edge(it->vpack());
|
||||
VPackSlice v;
|
||||
if (direction == TRI_EDGE_IN) {
|
||||
v = Transaction::extractFromFromDocument(edge);
|
||||
} else {
|
||||
v = Transaction::extractToFromDocument(edge);
|
||||
}
|
||||
|
||||
if (visited.find(v) == visited.end()) {
|
||||
// we have not yet visited this vertex
|
||||
if (depth >= node->minDepth()) {
|
||||
distinct.emplace_back(v);
|
||||
}
|
||||
if (depth < node->maxDepth()) {
|
||||
if (!initialized) {
|
||||
nextDepth.reserve(64);
|
||||
initialized = true;
|
||||
}
|
||||
nextDepth.emplace_back(v);
|
||||
}
|
||||
visited.emplace(v);
|
||||
continue;
|
||||
} else if (direction == TRI_EDGE_ANY) {
|
||||
v = Transaction::extractToFromDocument(edge);
|
||||
if (visited.find(v) == visited.end()) {
|
||||
// we have not yet visited this vertex
|
||||
if (depth >= node->minDepth()) {
|
||||
distinct.emplace_back(v);
|
||||
}
|
||||
if (depth < node->maxDepth()) {
|
||||
if (!initialized) {
|
||||
nextDepth.reserve(64);
|
||||
initialized = true;
|
||||
}
|
||||
nextDepth.emplace_back(v);
|
||||
}
|
||||
visited.emplace(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!nextDepth.empty()) {
|
||||
runNeighbors(nextDepth, visited, distinct, direction, depth + 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include "Aql/ExecutionBlock.h"
|
||||
#include "Aql/TraversalNode.h"
|
||||
#include "Basics/VelocyPackHelper.h"
|
||||
#include "VocBase/Traverser.h"
|
||||
|
||||
namespace arangodb {
|
||||
|
@ -146,6 +147,17 @@ class TraversalBlock : public ExecutionBlock {
|
|||
/// @brief Executes the path-local filter expressions
|
||||
/// Also determines the context
|
||||
void executeFilterExpressions();
|
||||
|
||||
/// @brief optimized version of neighbors search, must properly implement this
|
||||
void neighbors(std::string const& startVertex);
|
||||
|
||||
/// @brief worker for neighbors() function
|
||||
void runNeighbors(std::vector<VPackSlice> const& startVertices,
|
||||
std::unordered_set<VPackSlice, basics::VelocyPackHelper::VPackStringHash, basics::VelocyPackHelper::VPackStringEqual>& visited,
|
||||
std::vector<VPackSlice>& distinct,
|
||||
TRI_edge_direction_e direction,
|
||||
uint64_t depth);
|
||||
|
||||
};
|
||||
} // namespace arangodb::aql
|
||||
} // namespace arangodb
|
||||
|
|
|
@ -135,7 +135,8 @@ TraversalNode::TraversalNode(ExecutionPlan* plan, size_t id,
|
|||
_inVariable(nullptr),
|
||||
_graphObj(nullptr),
|
||||
_condition(nullptr),
|
||||
_options(options) {
|
||||
_options(options),
|
||||
_specializedNeighborsSearch(false) {
|
||||
|
||||
TRI_ASSERT(_vocbase != nullptr);
|
||||
TRI_ASSERT(direction != nullptr);
|
||||
|
@ -268,7 +269,8 @@ TraversalNode::TraversalNode(
|
|||
_directions(directions),
|
||||
_graphObj(nullptr),
|
||||
_condition(nullptr),
|
||||
_options(options) {
|
||||
_options(options),
|
||||
_specializedNeighborsSearch(false) {
|
||||
_graphJson = arangodb::basics::Json(arangodb::basics::Json::Array, edgeColls.size());
|
||||
|
||||
for (auto& it : edgeColls) {
|
||||
|
@ -286,7 +288,8 @@ TraversalNode::TraversalNode(ExecutionPlan* plan,
|
|||
_pathOutVariable(nullptr),
|
||||
_inVariable(nullptr),
|
||||
_graphObj(nullptr),
|
||||
_condition(nullptr) {
|
||||
_condition(nullptr),
|
||||
_specializedNeighborsSearch(false) {
|
||||
_minDepth =
|
||||
arangodb::basics::JsonHelper::stringUInt64(base.json(), "minDepth");
|
||||
_maxDepth =
|
||||
|
@ -454,6 +457,7 @@ TraversalNode::TraversalNode(ExecutionPlan* plan,
|
|||
_options = TraversalOptions(base);
|
||||
}
|
||||
|
||||
_specializedNeighborsSearch = arangodb::basics::JsonHelper::getBooleanValue(base.json(), "specializedNeighborsSearch", false);
|
||||
}
|
||||
|
||||
int TraversalNode::checkIsOutVariable(size_t variableId) const {
|
||||
|
@ -469,6 +473,30 @@ int TraversalNode::checkIsOutVariable(size_t variableId) const {
|
|||
return -1;
|
||||
}
|
||||
|
||||
/// @brief check if all directions are equal
|
||||
bool TraversalNode::allDirectionsEqual() const {
|
||||
if (_directions.empty()) {
|
||||
// no directions!
|
||||
return false;
|
||||
}
|
||||
size_t const n = _directions.size();
|
||||
TRI_edge_direction_e const expected = _directions[0];
|
||||
|
||||
for (size_t i = 1; i < n; ++i) {
|
||||
if (_directions[i] != expected) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void TraversalNode::specializeToNeighborsSearch() {
|
||||
TRI_ASSERT(allDirectionsEqual());
|
||||
TRI_ASSERT(!_directions.empty());
|
||||
|
||||
_specializedNeighborsSearch = true;
|
||||
}
|
||||
|
||||
/// @brief toVelocyPack, for TraversalNode
|
||||
void TraversalNode::toVelocyPackHelper(arangodb::velocypack::Builder& nodes,
|
||||
bool verbose) const {
|
||||
|
@ -539,6 +567,8 @@ void TraversalNode::toVelocyPackHelper(arangodb::velocypack::Builder& nodes,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
nodes.add("specializedNeighborsSearch", VPackValue(_specializedNeighborsSearch));
|
||||
|
||||
nodes.add(VPackValue("traversalFlags"));
|
||||
_options.toVelocyPack(nodes);
|
||||
|
@ -584,6 +614,10 @@ ExecutionNode* TraversalNode::clone(ExecutionPlan* plan, bool withDependencies,
|
|||
c->setPathOutput(pathOutVariable);
|
||||
}
|
||||
|
||||
if (_specializedNeighborsSearch) {
|
||||
c->specializeToNeighborsSearch();
|
||||
}
|
||||
|
||||
cloneHelper(c, plan, withDependencies, withProperties);
|
||||
|
||||
return static_cast<ExecutionNode*>(c);
|
||||
|
|
|
@ -61,7 +61,7 @@ class SimpleTraverserExpression
|
|||
/// @brief class TraversalNode
|
||||
class TraversalNode : public ExecutionNode {
|
||||
friend class ExecutionBlock;
|
||||
friend class TraversalCollectionBlock;
|
||||
friend class TraversalBlock;
|
||||
friend class RedundantCalculationsReplacer;
|
||||
|
||||
/// @brief constructor with a vocbase and a collection name
|
||||
|
@ -134,6 +134,14 @@ class TraversalNode : public ExecutionNode {
|
|||
/// @brief getVariablesSetHere
|
||||
std::vector<Variable const*> getVariablesSetHere() const override final {
|
||||
std::vector<Variable const*> vars;
|
||||
|
||||
size_t const numVars =
|
||||
(_vertexOutVariable != nullptr ? 1 : 0) +
|
||||
(_edgeOutVariable != nullptr ? 1 : 0) +
|
||||
(_pathOutVariable != nullptr ? 1 : 0);
|
||||
|
||||
vars.reserve(numVars);
|
||||
|
||||
if (_vertexOutVariable != nullptr) {
|
||||
vars.emplace_back(_vertexOutVariable);
|
||||
}
|
||||
|
@ -211,6 +219,10 @@ class TraversalNode : public ExecutionNode {
|
|||
void storeSimpleExpression(bool isEdgeAccess, size_t indexAccess,
|
||||
AstNodeType comparisonType,
|
||||
AstNode const* varAccess, AstNode* compareToNode);
|
||||
|
||||
bool allDirectionsEqual() const;
|
||||
|
||||
void specializeToNeighborsSearch();
|
||||
|
||||
/// @brief Returns a regerence to the simple traverser expressions
|
||||
std::unordered_map<
|
||||
|
@ -219,6 +231,11 @@ class TraversalNode : public ExecutionNode {
|
|||
return &_expressions;
|
||||
}
|
||||
|
||||
uint64_t minDepth() const { return _minDepth; }
|
||||
uint64_t maxDepth() const { return _maxDepth; }
|
||||
|
||||
TraversalOptions const* options() const { return &_options; }
|
||||
|
||||
private:
|
||||
/// @brief the database
|
||||
TRI_vocbase_t* _vocbase;
|
||||
|
@ -238,7 +255,7 @@ class TraversalNode : public ExecutionNode {
|
|||
/// @brief input vertexId only used if _inVariable is unused
|
||||
std::string _vertexId;
|
||||
|
||||
/// @brief input graphJson only used for serialisation & info
|
||||
/// @brief input graphJson only used for serialization & info
|
||||
arangodb::basics::Json _graphJson;
|
||||
|
||||
/// @brief The minimal depth included in the result
|
||||
|
@ -270,6 +287,8 @@ class TraversalNode : public ExecutionNode {
|
|||
|
||||
/// @brief Options for traversals
|
||||
TraversalOptions _options;
|
||||
|
||||
bool _specializedNeighborsSearch;
|
||||
};
|
||||
|
||||
} // namespace arangodb::aql
|
||||
|
|
|
@ -575,6 +575,10 @@ DocumentDitch* Transaction::orderDitch(TRI_voc_cid_t cid) {
|
|||
TRI_ASSERT(getStatus() == TRI_TRANSACTION_RUNNING ||
|
||||
getStatus() == TRI_TRANSACTION_CREATED);
|
||||
|
||||
if (_ditchCache.cid == cid) {
|
||||
return _ditchCache.ditch;
|
||||
}
|
||||
|
||||
TRI_transaction_collection_t* trxCollection = TRI_GetCollectionTransaction(_trx, cid, TRI_TRANSACTION_READ);
|
||||
|
||||
if (trxCollection == nullptr) {
|
||||
|
@ -592,6 +596,10 @@ DocumentDitch* Transaction::orderDitch(TRI_voc_cid_t cid) {
|
|||
if (ditch == nullptr) {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
_ditchCache.cid = cid;
|
||||
_ditchCache.ditch = ditch;
|
||||
|
||||
return ditch;
|
||||
}
|
||||
|
||||
|
|
|
@ -955,6 +955,16 @@ class Transaction {
|
|||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::shared_ptr<TransactionContext> _transactionContext;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief cache for last handed out DocumentDitch
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct {
|
||||
TRI_voc_cid_t cid = 0;
|
||||
DocumentDitch* ditch = nullptr;
|
||||
}
|
||||
_ditchCache;
|
||||
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -465,7 +465,7 @@ void SingleServerTraverser::EdgeGetter::getAllEdges(
|
|||
if (!_traverser->edgeMatchesConditions(edge, depth)) {
|
||||
if (_opts.uniqueEdges == TraverserOptions::UniquenessLevel::GLOBAL) {
|
||||
// Insert a dummy to please the uniqueness
|
||||
_traverser->_edges.emplace(id, nullptr);
|
||||
_traverser->_edges.emplace(std::move(id), nullptr);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
@ -489,3 +489,4 @@ void SingleServerTraverser::EdgeGetter::getAllEdges(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -111,13 +111,6 @@ class SingleServerTraverser final : public Traverser {
|
|||
|
||||
SingleServerTraverser* _traverser;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Cache for indexes. Maps collectionName to Index
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::unordered_map<std::string, std::pair<TRI_voc_cid_t, EdgeIndex*>>
|
||||
_indexCache;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Traverser options
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -104,7 +104,7 @@ size_t arangodb::traverser::TraverserOptions::collectionCount () const {
|
|||
}
|
||||
|
||||
bool arangodb::traverser::TraverserOptions::getCollection(
|
||||
size_t const index, std::string& name, TRI_edge_direction_e& dir) const {
|
||||
size_t index, std::string& name, TRI_edge_direction_e& dir) const {
|
||||
if (index >= _collections.size()) {
|
||||
// No more collections stop now
|
||||
return false;
|
||||
|
@ -121,7 +121,7 @@ bool arangodb::traverser::TraverserOptions::getCollection(
|
|||
|
||||
bool arangodb::traverser::TraverserOptions::getCollectionAndSearchValue(
|
||||
size_t index, std::string const& vertexId, std::string& name,
|
||||
Transaction::IndexHandle& indexHandle, VPackBuilder& builder) {
|
||||
Transaction::IndexHandle& indexHandle, VPackBuilder& builder) const {
|
||||
if (index >= _collections.size()) {
|
||||
// No more collections stop now
|
||||
return false;
|
||||
|
|
|
@ -231,11 +231,11 @@ struct TraverserOptions {
|
|||
|
||||
size_t collectionCount() const;
|
||||
|
||||
bool getCollection(size_t const, std::string&, TRI_edge_direction_e&) const;
|
||||
bool getCollection(size_t, std::string&, TRI_edge_direction_e&) const;
|
||||
|
||||
bool getCollectionAndSearchValue(size_t, std::string const&, std::string&,
|
||||
arangodb::Transaction::IndexHandle&,
|
||||
arangodb::velocypack::Builder&);
|
||||
arangodb::velocypack::Builder&) const;
|
||||
};
|
||||
|
||||
class Traverser {
|
||||
|
@ -322,6 +322,8 @@ class Traverser {
|
|||
_readDocuments = 0;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
TraverserOptions const* options() { return &_opts; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Prune the current path prefix. Do not evaluate it any further.
|
||||
|
|
|
@ -27,47 +27,184 @@
|
|||
#include "Basics/Common.h"
|
||||
#include "Basics/xxhash.h"
|
||||
|
||||
#include <velocypack/Slice.h>
|
||||
#include <velocypack/Value.h>
|
||||
|
||||
namespace arangodb {
|
||||
|
||||
/// @brief a struct describing a C character array
|
||||
/// not responsible for memory management!
|
||||
struct StringRef {
|
||||
StringRef() : data(""), length(0) {}
|
||||
explicit StringRef(std::string const& str) : data(str.c_str()), length(str.size()) {}
|
||||
explicit StringRef(char const* data) : data(data), length(strlen(data)) {}
|
||||
StringRef(char const* data, size_t length) : data(data), length(length) {}
|
||||
class StringRef {
|
||||
public:
|
||||
/// @brief create an empty StringRef
|
||||
StringRef() : _data(""), _length(0) {}
|
||||
|
||||
bool operator==(StringRef const& other) const {
|
||||
return (length == other.length && memcmp(data, other.data, length) == 0);
|
||||
/// @brief create a StringRef from an std::string
|
||||
explicit StringRef(std::string const& str) : _data(str.c_str()), _length(str.size()) {}
|
||||
|
||||
/// @brief create a StringRef from a null-terminated C string
|
||||
explicit StringRef(char const* data) : _data(data), _length(strlen(data)) {}
|
||||
|
||||
/// @brief create a StringRef from a VPack slice (must be of type String)
|
||||
explicit StringRef(arangodb::velocypack::Slice const& slice) : StringRef() {
|
||||
arangodb::velocypack::ValueLength l;
|
||||
_data = slice.getString(l);
|
||||
_length = l;
|
||||
}
|
||||
|
||||
bool operator==(std::string const& other) const {
|
||||
return (length == other.size() && memcmp(data, other.c_str(), length) == 0);
|
||||
/// @brief create a StringRef from a C string plus length
|
||||
StringRef(char const* data, size_t length) : _data(data), _length(length) {}
|
||||
|
||||
/// @brief create a StringRef from another StringRef
|
||||
StringRef(StringRef const& other)
|
||||
: _data(other._data), _length(other._length) {}
|
||||
|
||||
/// @brief create a StringRef from another StringRef
|
||||
StringRef& operator=(StringRef const& other) {
|
||||
_data = other._data;
|
||||
_length = other._length;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @brief create a StringRef from an std::string
|
||||
StringRef& operator=(std::string const& other) {
|
||||
_data = other.c_str();
|
||||
_length = other.size();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @brief create a StringRef from a null-terminated C string
|
||||
StringRef& operator=(char const* other) {
|
||||
_data = other;
|
||||
_length = strlen(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @brief create a StringRef from a VPack slice of type String
|
||||
StringRef& operator=(arangodb::velocypack::Slice const& slice) {
|
||||
arangodb::velocypack::ValueLength l;
|
||||
_data = slice.getString(l);
|
||||
_length = l;
|
||||
return *this;
|
||||
}
|
||||
|
||||
char const* data;
|
||||
size_t length;
|
||||
size_t find(char c) const {
|
||||
char const* p = static_cast<char const*>(memchr(static_cast<void const*>(_data), c, _length));
|
||||
|
||||
if (p == nullptr) {
|
||||
return std::string::npos;
|
||||
}
|
||||
|
||||
return (p - _data);
|
||||
}
|
||||
|
||||
StringRef substr(size_t pos = 0, size_t count = std::string::npos) const {
|
||||
if (pos >= _length) {
|
||||
throw std::out_of_range("substr index out of bounds");
|
||||
}
|
||||
if (count == std::string::npos || (count + pos >= _length)) {
|
||||
count = _length - pos;
|
||||
}
|
||||
return StringRef(_data + pos, count);
|
||||
}
|
||||
|
||||
int compare(std::string const& other) const {
|
||||
int res = memcmp(_data, other.c_str(), (std::min)(_length, other.size()));
|
||||
if (res != 0) {
|
||||
return res;
|
||||
}
|
||||
return (_length - other.size());
|
||||
}
|
||||
|
||||
int compare(StringRef const& other) const {
|
||||
int res = memcmp(_data, other._data, (std::min)(_length, other._length));
|
||||
if (res != 0) {
|
||||
return res;
|
||||
}
|
||||
return (_length - other._length);
|
||||
}
|
||||
|
||||
inline std::string toString() const {
|
||||
return std::string(_data, _length);
|
||||
}
|
||||
|
||||
inline bool empty() const {
|
||||
return (_length == 0);
|
||||
}
|
||||
|
||||
char at(size_t index) const {
|
||||
if (index >= _length) {
|
||||
throw std::out_of_range("StringRef index out of bounds");
|
||||
}
|
||||
return operator[](index);
|
||||
}
|
||||
|
||||
inline char const* begin() const {
|
||||
return _data;
|
||||
}
|
||||
|
||||
inline char const* end() const {
|
||||
return _data + _length;
|
||||
}
|
||||
|
||||
inline char front() const { return _data[0]; }
|
||||
|
||||
inline char back() const { return _data[_length - 1]; }
|
||||
|
||||
inline char operator[](size_t index) const noexcept {
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
inline char const* data() const noexcept {
|
||||
return _data;
|
||||
}
|
||||
|
||||
inline size_t size() const noexcept {
|
||||
return _length;
|
||||
}
|
||||
|
||||
inline size_t length() const noexcept {
|
||||
return _length;
|
||||
}
|
||||
|
||||
private:
|
||||
char const* _data;
|
||||
size_t _length;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
inline bool operator==(arangodb::StringRef const& lhs, arangodb::StringRef const& rhs) {
|
||||
return (lhs.size() == rhs.size() && memcmp(lhs.data(), rhs.data(), lhs.size()) == 0);
|
||||
}
|
||||
|
||||
inline bool operator!=(arangodb::StringRef const& lhs, arangodb::StringRef const& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
inline bool operator==(arangodb::StringRef const& lhs, std::string const& rhs) {
|
||||
return (lhs.size() == rhs.size() && memcmp(lhs.data(), rhs.c_str(), lhs.size()) == 0);
|
||||
}
|
||||
|
||||
inline bool operator!=(arangodb::StringRef const& lhs, std::string const& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
namespace std {
|
||||
|
||||
template <>
|
||||
struct hash<arangodb::StringRef> {
|
||||
size_t operator()(arangodb::StringRef const& value) const noexcept {
|
||||
return XXH64(value.data, value.length, 0xdeadbeef);
|
||||
return XXH64(value.data(), value.size(), 0xdeadbeef);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct equal_to<arangodb::StringRef> {
|
||||
bool operator()(arangodb::StringRef const& lhs,
|
||||
arangodb::StringRef const& rhs) const {
|
||||
if (lhs.length != rhs.length) {
|
||||
return false;
|
||||
}
|
||||
return (memcmp(lhs.data, rhs.data, lhs.length) == 0);
|
||||
arangodb::StringRef const& rhs) const noexcept {
|
||||
return (lhs.size() == rhs.size() &&
|
||||
(memcmp(lhs.data(), rhs.data(), lhs.size()) == 0));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue