mirror of https://gitee.com/bigwinds/arangodb
Merge branch 'engine-api' of https://github.com/arangodb/arangodb into engine-api
This commit is contained in:
commit
ee99288f88
|
@ -1,6 +1,12 @@
|
||||||
devel
|
devel
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
* Removed undocumented internal HTTP APIs:
|
||||||
|
* POST _api/edges
|
||||||
|
* PUT _api/edges
|
||||||
|
|
||||||
|
The documented GET _api/edges remains unmodified.
|
||||||
|
|
||||||
* moved V8 code into a git submodule
|
* moved V8 code into a git submodule
|
||||||
this requires running the command
|
this requires running the command
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
#include "Graphs.h"
|
#include "Graphs.h"
|
||||||
#include "Aql/AstNode.h"
|
#include "Aql/AstNode.h"
|
||||||
|
#include "Basics/StaticStrings.h"
|
||||||
#include "Basics/VelocyPackHelper.h"
|
#include "Basics/VelocyPackHelper.h"
|
||||||
|
|
||||||
#include <velocypack/Iterator.h>
|
#include <velocypack/Iterator.h>
|
||||||
|
@ -39,10 +40,16 @@ EdgeConditionBuilder::EdgeConditionBuilder(AstNode* modCondition)
|
||||||
_toCondition(nullptr),
|
_toCondition(nullptr),
|
||||||
_modCondition(modCondition),
|
_modCondition(modCondition),
|
||||||
_containsCondition(false) {
|
_containsCondition(false) {
|
||||||
|
#ifdef TRI_ENABLE_MAINTAINER_MODE
|
||||||
|
if (_modCondition != nullptr) {
|
||||||
TRI_ASSERT(_modCondition->type == NODE_TYPE_OPERATOR_NARY_AND);
|
TRI_ASSERT(_modCondition->type == NODE_TYPE_OPERATOR_NARY_AND);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void EdgeConditionBuilder::addConditionPart(AstNode const* part) {
|
void EdgeConditionBuilder::addConditionPart(AstNode const* part) {
|
||||||
|
TRI_ASSERT(_modCondition != nullptr);
|
||||||
|
TRI_ASSERT(_modCondition->type == NODE_TYPE_OPERATOR_NARY_AND);
|
||||||
TRI_ASSERT(!_containsCondition);
|
TRI_ASSERT(!_containsCondition);
|
||||||
// The ordering is only maintained before we request a specific
|
// The ordering is only maintained before we request a specific
|
||||||
// condition
|
// condition
|
||||||
|
@ -71,7 +78,7 @@ void EdgeConditionBuilder::swapSides(AstNode* cond) {
|
||||||
TRI_ASSERT(_modCondition->numMembers() > 0);
|
TRI_ASSERT(_modCondition->numMembers() > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
AstNode const* EdgeConditionBuilder::getOutboundCondition() {
|
AstNode* EdgeConditionBuilder::getOutboundCondition() {
|
||||||
if (_fromCondition == nullptr) {
|
if (_fromCondition == nullptr) {
|
||||||
buildFromCondition();
|
buildFromCondition();
|
||||||
}
|
}
|
||||||
|
@ -80,7 +87,7 @@ AstNode const* EdgeConditionBuilder::getOutboundCondition() {
|
||||||
return _modCondition;
|
return _modCondition;
|
||||||
}
|
}
|
||||||
|
|
||||||
AstNode const* EdgeConditionBuilder::getInboundCondition() {
|
AstNode* EdgeConditionBuilder::getInboundCondition() {
|
||||||
if (_toCondition == nullptr) {
|
if (_toCondition == nullptr) {
|
||||||
buildToCondition();
|
buildToCondition();
|
||||||
}
|
}
|
||||||
|
@ -89,6 +96,72 @@ AstNode const* EdgeConditionBuilder::getInboundCondition() {
|
||||||
return _modCondition;
|
return _modCondition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EdgeConditionBuilderContainer::EdgeConditionBuilderContainer() :
|
||||||
|
EdgeConditionBuilder(nullptr) {
|
||||||
|
auto node = std::make_unique<AstNode>(NODE_TYPE_OPERATOR_NARY_AND);
|
||||||
|
_modCondition = node.get();
|
||||||
|
_astNodes.emplace_back(node.get());
|
||||||
|
node.release();
|
||||||
|
|
||||||
|
auto comp = std::make_unique<AstNode>(NODE_TYPE_VALUE);
|
||||||
|
comp->setValueType(VALUE_TYPE_STRING);
|
||||||
|
comp->setStringValue("", 0);
|
||||||
|
_astNodes.emplace_back(comp.get());
|
||||||
|
_compareNode = comp.release();
|
||||||
|
|
||||||
|
_var = _varGen.createTemporaryVariable();
|
||||||
|
|
||||||
|
auto varNode = std::make_unique<AstNode>(NODE_TYPE_REFERENCE);
|
||||||
|
varNode->setData(_var);
|
||||||
|
_astNodes.emplace_back(varNode.get());
|
||||||
|
_varNode = varNode.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
EdgeConditionBuilderContainer::~EdgeConditionBuilderContainer() {
|
||||||
|
// we have to clean up the AstNodes
|
||||||
|
for (auto it : _astNodes) {
|
||||||
|
delete it;
|
||||||
|
}
|
||||||
|
_astNodes.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
AstNode* EdgeConditionBuilderContainer::createEqCheck(AstNode const* access) {
|
||||||
|
auto node = std::make_unique<AstNode>(NODE_TYPE_OPERATOR_BINARY_EQ);
|
||||||
|
node->reserve(2);
|
||||||
|
node->addMember(access);
|
||||||
|
node->addMember(_compareNode);
|
||||||
|
_astNodes.emplace_back(node.get());
|
||||||
|
return node.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
AstNode* EdgeConditionBuilderContainer::createAttributeAccess(std::string const& attr) {
|
||||||
|
auto node = std::make_unique<AstNode>(NODE_TYPE_ATTRIBUTE_ACCESS);
|
||||||
|
node->addMember(_varNode);
|
||||||
|
node->setStringValue(attr.c_str(), attr.length());
|
||||||
|
_astNodes.emplace_back(node.get());
|
||||||
|
return node.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EdgeConditionBuilderContainer::buildFromCondition() {
|
||||||
|
TRI_ASSERT(_fromCondition == nullptr);
|
||||||
|
auto access = createAttributeAccess(StaticStrings::FromString);
|
||||||
|
_fromCondition = createEqCheck(access);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EdgeConditionBuilderContainer::buildToCondition() {
|
||||||
|
TRI_ASSERT(_toCondition == nullptr);
|
||||||
|
auto access = createAttributeAccess(StaticStrings::ToString);
|
||||||
|
_toCondition = createEqCheck(access);
|
||||||
|
}
|
||||||
|
|
||||||
|
Variable const* EdgeConditionBuilderContainer::getVariable() const {
|
||||||
|
return _var;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EdgeConditionBuilderContainer::setVertexId(std::string const& id) {
|
||||||
|
_compareNode->setStringValue(id.c_str(), id.length());
|
||||||
|
}
|
||||||
|
|
||||||
void Graph::insertVertexCollections(VPackSlice& arr) {
|
void Graph::insertVertexCollections(VPackSlice& arr) {
|
||||||
TRI_ASSERT(arr.isArray());
|
TRI_ASSERT(arr.isArray());
|
||||||
for (auto const& c : VPackArrayIterator(arr)) {
|
for (auto const& c : VPackArrayIterator(arr)) {
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#define ARANGOD_AQL_GRAPHS_H 1
|
#define ARANGOD_AQL_GRAPHS_H 1
|
||||||
|
|
||||||
#include "Basics/Common.h"
|
#include "Basics/Common.h"
|
||||||
|
#include "Aql/VariableGenerator.h"
|
||||||
|
|
||||||
namespace arangodb {
|
namespace arangodb {
|
||||||
|
|
||||||
|
@ -82,16 +83,64 @@ class EdgeConditionBuilder {
|
||||||
void addConditionPart(AstNode const*);
|
void addConditionPart(AstNode const*);
|
||||||
|
|
||||||
// Get the complete condition for outbound edges
|
// Get the complete condition for outbound edges
|
||||||
AstNode const* getOutboundCondition();
|
AstNode* getOutboundCondition();
|
||||||
|
|
||||||
// Get the complete condition for inbound edges
|
// Get the complete condition for inbound edges
|
||||||
AstNode const* getInboundCondition();
|
AstNode* getInboundCondition();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Internal helper to swap _from and _to parts
|
// Internal helper to swap _from and _to parts
|
||||||
void swapSides(AstNode* condition);
|
void swapSides(AstNode* condition);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Wrapper around EdgeConditionBuilder that takes responsibility for all
|
||||||
|
// AstNodes created with it. Can be used outside of an AQL query.
|
||||||
|
class EdgeConditionBuilderContainer final : public EdgeConditionBuilder {
|
||||||
|
public:
|
||||||
|
EdgeConditionBuilderContainer();
|
||||||
|
|
||||||
|
~EdgeConditionBuilderContainer();
|
||||||
|
|
||||||
|
// Get a pointer to the used variable
|
||||||
|
Variable const* getVariable() const;
|
||||||
|
|
||||||
|
// Set the id of the searched vertex
|
||||||
|
// NOTE: This class does not take responsiblity for the string.
|
||||||
|
// So caller needs to make sure it does not run out of scope
|
||||||
|
// as long as these conditions are used.
|
||||||
|
void setVertexId(std::string const&);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Create the _fromCondition for the first time.
|
||||||
|
void buildFromCondition() override;
|
||||||
|
|
||||||
|
// Create the _toCondition for the first time.
|
||||||
|
void buildToCondition() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Create the equality node using the given access
|
||||||
|
AstNode* createEqCheck(AstNode const* access);
|
||||||
|
|
||||||
|
// Create a node with access of attr on the variable
|
||||||
|
AstNode* createAttributeAccess(std::string const& attr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// List of AstNodes this container is responsible for
|
||||||
|
std::vector<AstNode*> _astNodes;
|
||||||
|
|
||||||
|
// The variable node that is used to hold the edge
|
||||||
|
AstNode* _varNode;
|
||||||
|
|
||||||
|
// The value the edge is compared to
|
||||||
|
AstNode* _compareNode;
|
||||||
|
|
||||||
|
// Reference to the exchangeable variable node
|
||||||
|
Variable* _var;
|
||||||
|
|
||||||
|
// Reference to the VariableGenerator
|
||||||
|
VariableGenerator _varGen;
|
||||||
|
};
|
||||||
|
|
||||||
class Graph {
|
class Graph {
|
||||||
public:
|
public:
|
||||||
explicit Graph(arangodb::velocypack::Slice const&);
|
explicit Graph(arangodb::velocypack::Slice const&);
|
||||||
|
|
|
@ -22,14 +22,15 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include "RestEdgesHandler.h"
|
#include "RestEdgesHandler.h"
|
||||||
|
#include "Aql/AstNode.h"
|
||||||
|
#include "Aql/Graphs.h"
|
||||||
|
#include "Aql/Variable.h"
|
||||||
#include "Basics/ScopeGuard.h"
|
#include "Basics/ScopeGuard.h"
|
||||||
#include "Cluster/ClusterMethods.h"
|
#include "Cluster/ClusterMethods.h"
|
||||||
#include "MMFiles/MMFilesEdgeIndex.h"
|
|
||||||
#include "Utils/CollectionNameResolver.h"
|
#include "Utils/CollectionNameResolver.h"
|
||||||
#include "Utils/OperationCursor.h"
|
#include "Utils/OperationCursor.h"
|
||||||
#include "Utils/SingleCollectionTransaction.h"
|
#include "Utils/SingleCollectionTransaction.h"
|
||||||
#include "Utils/StandaloneTransactionContext.h"
|
#include "Utils/StandaloneTransactionContext.h"
|
||||||
#include "VocBase/Traverser.h"
|
|
||||||
|
|
||||||
#include <velocypack/Iterator.h>
|
#include <velocypack/Iterator.h>
|
||||||
#include <velocypack/velocypack-aliases.h>
|
#include <velocypack/velocypack-aliases.h>
|
||||||
|
@ -47,18 +48,9 @@ RestStatus RestEdgesHandler::execute() {
|
||||||
|
|
||||||
// execute one of the CRUD methods
|
// execute one of the CRUD methods
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case rest::RequestType::GET: {
|
case rest::RequestType::GET:
|
||||||
readEdges();
|
readEdges();
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case rest::RequestType::PUT:
|
|
||||||
// Now unsupported. Just temporary to check
|
|
||||||
TRI_ASSERT(false);
|
|
||||||
readEdges();
|
|
||||||
break;
|
|
||||||
case rest::RequestType::POST:
|
|
||||||
readEdgesForMultipleVertices();
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
generateNotImplemented("ILLEGAL " + EDGES_PATH);
|
generateNotImplemented("ILLEGAL " + EDGES_PATH);
|
||||||
break;
|
break;
|
||||||
|
@ -68,43 +60,38 @@ RestStatus RestEdgesHandler::execute() {
|
||||||
return RestStatus::DONE;
|
return RestStatus::DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RestEdgesHandler::getEdgesForVertexList(
|
void RestEdgesHandler::readCursor(aql::AstNode* condition,
|
||||||
VPackSlice const ids,
|
aql::Variable const* var,
|
||||||
TRI_edge_direction_e direction, SingleCollectionTransaction& trx,
|
std::string const& collectionName,
|
||||||
VPackBuilder& result, size_t& scannedIndex, size_t& filtered) {
|
SingleCollectionTransaction& trx,
|
||||||
TRI_ASSERT(result.isOpenArray());
|
VPackBuilder& result,
|
||||||
TRI_ASSERT(ids.isArray());
|
std::function<void(DocumentIdentifierToken const&)> cb) {
|
||||||
trx.orderDitch(trx.cid()); // will throw when it fails
|
|
||||||
|
|
||||||
std::string const collectionName =
|
Transaction::IndexHandle indexId;
|
||||||
trx.resolver()->getCollectionName(trx.cid());
|
bool foundIdx = trx.getBestIndexHandleForFilterCondition(
|
||||||
Transaction::IndexHandle indexId = trx.edgeIndexHandle(collectionName);
|
collectionName, condition, var, 1000, indexId);
|
||||||
|
if (!foundIdx) {
|
||||||
|
// Right now we enforce an edge index that can exactly! work on this condition.
|
||||||
|
// So it is impossible to not find an index.
|
||||||
|
TRI_ASSERT(false);
|
||||||
|
THROW_ARANGO_EXCEPTION_MESSAGE(
|
||||||
|
TRI_ERROR_ARANGO_NO_INDEX,
|
||||||
|
"Unable to find an edge-index to identify matching edges.");
|
||||||
|
}
|
||||||
|
|
||||||
VPackBuilder searchValueBuilder;
|
ManagedDocumentResult mmdr;
|
||||||
MMFilesEdgeIndex::buildSearchValueFromArray(direction, ids, searchValueBuilder);
|
std::unique_ptr<OperationCursor> cursor(trx.indexScanForCondition(
|
||||||
VPackSlice search = searchValueBuilder.slice();
|
indexId, condition, var, &mmdr, UINT64_MAX, 1000, false));
|
||||||
|
|
||||||
std::unique_ptr<OperationCursor> cursor =
|
|
||||||
trx.indexScan(collectionName, arangodb::Transaction::CursorType::INDEX,
|
|
||||||
indexId, search, nullptr, 0, UINT64_MAX, 1000, false);
|
|
||||||
if (cursor->failed()) {
|
if (cursor->failed()) {
|
||||||
THROW_ARANGO_EXCEPTION(cursor->code);
|
THROW_ARANGO_EXCEPTION(cursor->code);
|
||||||
}
|
}
|
||||||
|
|
||||||
ManagedDocumentResult mmdr;
|
|
||||||
auto collection = trx.documentCollection();
|
|
||||||
auto cb = [&](DocumentIdentifierToken const& token) {
|
|
||||||
if (collection->readDocument(&trx, mmdr, token)) {
|
|
||||||
result.add(VPackSlice(mmdr.vpack()));
|
|
||||||
}
|
|
||||||
scannedIndex++;
|
|
||||||
};
|
|
||||||
while (cursor->getMore(cb, 1000)) {
|
while (cursor->getMore(cb, 1000)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool RestEdgesHandler::getEdgesForVertex(
|
bool RestEdgesHandler::getEdgesForVertex(
|
||||||
std::string const& id, std::string const& collectionName,
|
std::string const& id, std::string const& collectionName,
|
||||||
TRI_edge_direction_e direction, SingleCollectionTransaction& trx,
|
TRI_edge_direction_e direction, SingleCollectionTransaction& trx,
|
||||||
|
@ -112,30 +99,68 @@ bool RestEdgesHandler::getEdgesForVertex(
|
||||||
TRI_ASSERT(result.isOpenArray());
|
TRI_ASSERT(result.isOpenArray());
|
||||||
trx.orderDitch(trx.cid()); // will throw when it fails
|
trx.orderDitch(trx.cid()); // will throw when it fails
|
||||||
|
|
||||||
Transaction::IndexHandle indexId = trx.edgeIndexHandle(collectionName);
|
|
||||||
|
|
||||||
VPackBuilder searchValueBuilder;
|
|
||||||
MMFilesEdgeIndex::buildSearchValue(direction, id, searchValueBuilder);
|
|
||||||
VPackSlice search = searchValueBuilder.slice();
|
|
||||||
|
|
||||||
std::unique_ptr<OperationCursor> cursor =
|
|
||||||
trx.indexScan(collectionName, arangodb::Transaction::CursorType::INDEX,
|
|
||||||
indexId, search, nullptr, 0, UINT64_MAX, 1000, false);
|
|
||||||
if (cursor->failed()) {
|
|
||||||
THROW_ARANGO_EXCEPTION(cursor->code);
|
|
||||||
}
|
|
||||||
|
|
||||||
ManagedDocumentResult mmdr;
|
ManagedDocumentResult mmdr;
|
||||||
auto collection = trx.documentCollection();
|
auto collection = trx.documentCollection();
|
||||||
|
|
||||||
|
// Create a conditionBuilder that manages the AstNodes for querying
|
||||||
|
aql::EdgeConditionBuilderContainer condBuilder;
|
||||||
|
condBuilder.setVertexId(id);
|
||||||
|
|
||||||
|
aql::Variable const* var = condBuilder.getVariable();
|
||||||
|
|
||||||
|
switch (direction) {
|
||||||
|
case TRI_EDGE_IN:
|
||||||
|
{
|
||||||
auto cb = [&] (DocumentIdentifierToken const& token) {
|
auto cb = [&] (DocumentIdentifierToken const& token) {
|
||||||
if (collection->readDocument(&trx, mmdr, token)) {
|
if (collection->readDocument(&trx, mmdr, token)) {
|
||||||
result.add(VPackSlice(mmdr.vpack()));
|
result.add(VPackSlice(mmdr.vpack()));
|
||||||
}
|
}
|
||||||
scannedIndex++;
|
scannedIndex++;
|
||||||
};
|
};
|
||||||
while (cursor->getMore(cb, 1000)) {
|
readCursor(condBuilder.getInboundCondition(), var, collectionName, trx,
|
||||||
|
result, cb);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TRI_EDGE_OUT:
|
||||||
|
{
|
||||||
|
auto cb = [&] (DocumentIdentifierToken const& token) {
|
||||||
|
if (collection->readDocument(&trx, mmdr, token)) {
|
||||||
|
result.add(VPackSlice(mmdr.vpack()));
|
||||||
|
}
|
||||||
|
scannedIndex++;
|
||||||
|
};
|
||||||
|
readCursor(condBuilder.getOutboundCondition(), var, collectionName, trx,
|
||||||
|
result, cb);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TRI_EDGE_ANY:
|
||||||
|
// We have to call both directions AND we have to unify reverse direction
|
||||||
|
{
|
||||||
|
std::unordered_set<DocumentIdentifierToken> found;
|
||||||
|
auto inboundCB = [&] (DocumentIdentifierToken const& token) {
|
||||||
|
if (collection->readDocument(&trx, mmdr, token)) {
|
||||||
|
result.add(VPackSlice(mmdr.vpack()));
|
||||||
|
// Mark edges we find
|
||||||
|
found.emplace(token);
|
||||||
|
}
|
||||||
|
scannedIndex++;
|
||||||
|
};
|
||||||
|
auto outboundCB = [&] (DocumentIdentifierToken const& token) {
|
||||||
|
if (found.find(token) == found.end()) {
|
||||||
|
// Only add those tokens we have not found yet
|
||||||
|
if (collection->readDocument(&trx, mmdr, token)) {
|
||||||
|
result.add(VPackSlice(mmdr.vpack()));
|
||||||
|
}
|
||||||
|
scannedIndex++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
readCursor(condBuilder.getInboundCondition(), var, collectionName, trx,
|
||||||
|
result, inboundCB);
|
||||||
|
readCursor(condBuilder.getOutboundCondition(), var, collectionName, trx,
|
||||||
|
result, outboundCB);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,160 +305,3 @@ bool RestEdgesHandler::readEdges() {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal function to receive all edges for a list of vertices
|
|
||||||
// Not publicly documented on purpose.
|
|
||||||
// NOTE: It ONLY except _id strings. Nothing else
|
|
||||||
bool RestEdgesHandler::readEdgesForMultipleVertices() {
|
|
||||||
std::vector<std::string> const& suffixes = _request->decodedSuffixes();
|
|
||||||
|
|
||||||
if (suffixes.size() != 1) {
|
|
||||||
generateError(rest::ResponseCode::BAD,
|
|
||||||
TRI_ERROR_HTTP_BAD_PARAMETER,
|
|
||||||
"expected POST " + EDGES_PATH +
|
|
||||||
"/<collection-identifier>?direction=<direction>");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool parseSuccess = true;
|
|
||||||
std::shared_ptr<VPackBuilder> parsedBody =
|
|
||||||
parseVelocyPackBody(&VPackOptions::Defaults, parseSuccess);
|
|
||||||
|
|
||||||
if (!parseSuccess) {
|
|
||||||
generateError(rest::ResponseCode::BAD,
|
|
||||||
TRI_ERROR_HTTP_BAD_PARAMETER,
|
|
||||||
"expected POST " + EDGES_PATH +
|
|
||||||
"/<collection-identifier>?direction=<direction>");
|
|
||||||
// A body is required
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
VPackSlice body = parsedBody->slice();
|
|
||||||
|
|
||||||
if (!body.isArray()) {
|
|
||||||
generateError(rest::ResponseCode::BAD,
|
|
||||||
TRI_ERROR_HTTP_BAD_PARAMETER,
|
|
||||||
"Expected an array of vertex _id's in body parameter");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string collectionName = suffixes[0];
|
|
||||||
CollectionNameResolver resolver(_vocbase);
|
|
||||||
TRI_col_type_e colType = resolver.getCollectionTypeCluster(collectionName);
|
|
||||||
|
|
||||||
if (colType == TRI_COL_TYPE_UNKNOWN) {
|
|
||||||
generateError(rest::ResponseCode::NOT_FOUND,
|
|
||||||
TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND);
|
|
||||||
return false;
|
|
||||||
} else if (colType != TRI_COL_TYPE_EDGE) {
|
|
||||||
generateError(rest::ResponseCode::BAD,
|
|
||||||
TRI_ERROR_ARANGO_COLLECTION_TYPE_INVALID);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool found;
|
|
||||||
std::string dirString = _request->value("direction", found);
|
|
||||||
|
|
||||||
if (!found || dirString.empty()) {
|
|
||||||
dirString = "any";
|
|
||||||
}
|
|
||||||
|
|
||||||
TRI_edge_direction_e direction;
|
|
||||||
|
|
||||||
if (dirString == "any") {
|
|
||||||
direction = TRI_EDGE_ANY;
|
|
||||||
} else if (dirString == "out" || dirString == "outbound") {
|
|
||||||
direction = TRI_EDGE_OUT;
|
|
||||||
} else if (dirString == "in" || dirString == "inbound") {
|
|
||||||
direction = TRI_EDGE_IN;
|
|
||||||
} else {
|
|
||||||
generateError(rest::ResponseCode::BAD,
|
|
||||||
TRI_ERROR_HTTP_BAD_PARAMETER,
|
|
||||||
"<direction> must by any, in, or out, not: " + dirString);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ServerState::instance()->isCoordinator()) {
|
|
||||||
rest::ResponseCode responseCode;
|
|
||||||
VPackBuilder resultDocument;
|
|
||||||
resultDocument.openObject();
|
|
||||||
|
|
||||||
for (auto const& it : VPackArrayIterator(body)) {
|
|
||||||
if (it.isString()) {
|
|
||||||
std::string vertexString(it.copyString());
|
|
||||||
|
|
||||||
int res = getFilteredEdgesOnCoordinator(
|
|
||||||
_vocbase->name(), collectionName, vertexString, direction,
|
|
||||||
responseCode, resultDocument);
|
|
||||||
if (res != TRI_ERROR_NO_ERROR) {
|
|
||||||
generateError(responseCode, res);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resultDocument.add("error", VPackValue(false));
|
|
||||||
resultDocument.add("code", VPackValue(200));
|
|
||||||
resultDocument.close();
|
|
||||||
|
|
||||||
generateResult(rest::ResponseCode::OK, resultDocument.slice());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find and load collection given by name or identifier
|
|
||||||
SingleCollectionTransaction trx(
|
|
||||||
StandaloneTransactionContext::Create(_vocbase), collectionName,
|
|
||||||
AccessMode::Type::READ);
|
|
||||||
|
|
||||||
// .............................................................................
|
|
||||||
// inside read transaction
|
|
||||||
// .............................................................................
|
|
||||||
|
|
||||||
int res = trx.begin();
|
|
||||||
if (res != TRI_ERROR_NO_ERROR) {
|
|
||||||
generateTransactionError(collectionName, res, "");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we are a DBserver, we want to use the cluster-wide collection
|
|
||||||
// name for error reporting:
|
|
||||||
if (ServerState::instance()->isDBServer()) {
|
|
||||||
collectionName = trx.resolver()->getCollectionName(trx.cid());
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t filtered = 0;
|
|
||||||
size_t scannedIndex = 0;
|
|
||||||
|
|
||||||
VPackBuilder resultBuilder;
|
|
||||||
resultBuilder.openObject();
|
|
||||||
// build edges
|
|
||||||
resultBuilder.add(VPackValue("edges")); // only key
|
|
||||||
resultBuilder.openArray();
|
|
||||||
|
|
||||||
bool ok = getEdgesForVertexList(body, direction, trx, resultBuilder,
|
|
||||||
scannedIndex, filtered);
|
|
||||||
|
|
||||||
if (!ok) {
|
|
||||||
// Ignore the error
|
|
||||||
}
|
|
||||||
|
|
||||||
resultBuilder.close();
|
|
||||||
|
|
||||||
res = trx.finish(res);
|
|
||||||
if (res != TRI_ERROR_NO_ERROR) {
|
|
||||||
generateTransactionError(collectionName, res, "");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
resultBuilder.add("error", VPackValue(false));
|
|
||||||
resultBuilder.add("code", VPackValue(200));
|
|
||||||
resultBuilder.add("stats", VPackValue(VPackValueType::Object));
|
|
||||||
resultBuilder.add("scannedIndex", VPackValue(scannedIndex));
|
|
||||||
resultBuilder.add("filtered", VPackValue(filtered));
|
|
||||||
resultBuilder.close(); // inner object
|
|
||||||
resultBuilder.close();
|
|
||||||
|
|
||||||
// and generate a response
|
|
||||||
generateResult(rest::ResponseCode::OK, resultBuilder.slice(),
|
|
||||||
trx.transactionContext());
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
|
@ -30,8 +30,14 @@
|
||||||
#include <velocypack/Builder.h>
|
#include <velocypack/Builder.h>
|
||||||
|
|
||||||
namespace arangodb {
|
namespace arangodb {
|
||||||
|
struct DocumentIdentifierToken;
|
||||||
class SingleCollectionTransaction;
|
class SingleCollectionTransaction;
|
||||||
|
|
||||||
|
namespace aql {
|
||||||
|
struct AstNode;
|
||||||
|
struct Variable;
|
||||||
|
}
|
||||||
|
|
||||||
class RestEdgesHandler : public RestVocbaseBaseHandler {
|
class RestEdgesHandler : public RestVocbaseBaseHandler {
|
||||||
public:
|
public:
|
||||||
explicit RestEdgesHandler(GeneralRequest*, GeneralResponse*);
|
explicit RestEdgesHandler(GeneralRequest*, GeneralResponse*);
|
||||||
|
@ -48,10 +54,14 @@ class RestEdgesHandler : public RestVocbaseBaseHandler {
|
||||||
bool readEdges();
|
bool readEdges();
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief reads all edges in given direction for a given list of vertices
|
/// @brief find the index and read it completely with the given callback
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
bool readEdgesForMultipleVertices();
|
void readCursor(aql::AstNode* condition, aql::Variable const* var,
|
||||||
|
std::string const& collectionName,
|
||||||
|
SingleCollectionTransaction& trx,
|
||||||
|
arangodb::velocypack::Builder& result,
|
||||||
|
std::function<void(DocumentIdentifierToken const&)> cb);
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief get all edges for a given vertex. Independent from the request
|
/// @brief get all edges for a given vertex. Independent from the request
|
||||||
|
@ -61,15 +71,6 @@ class RestEdgesHandler : public RestVocbaseBaseHandler {
|
||||||
std::string const& id, std::string const& collectionName,
|
std::string const& id, std::string const& collectionName,
|
||||||
TRI_edge_direction_e direction, SingleCollectionTransaction& trx,
|
TRI_edge_direction_e direction, SingleCollectionTransaction& trx,
|
||||||
arangodb::velocypack::Builder&, size_t& scannedIndex, size_t& filtered);
|
arangodb::velocypack::Builder&, size_t& scannedIndex, size_t& filtered);
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief get all edges for a list of vertices. Independent from the request
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool getEdgesForVertexList(
|
|
||||||
arangodb::velocypack::Slice const ids,
|
|
||||||
TRI_edge_direction_e direction, SingleCollectionTransaction& trx,
|
|
||||||
arangodb::velocypack::Builder&, size_t& scannedIndex, size_t& filtered);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue