1
0
Fork 0

Merge branch 'engine-api' of https://github.com/arangodb/arangodb into engine-api

This commit is contained in:
jsteemann 2017-02-09 17:30:24 +01:00
commit ee99288f88
5 changed files with 229 additions and 232 deletions

View File

@ -1,6 +1,12 @@
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
this requires running the command

View File

@ -23,6 +23,7 @@
#include "Graphs.h"
#include "Aql/AstNode.h"
#include "Basics/StaticStrings.h"
#include "Basics/VelocyPackHelper.h"
#include <velocypack/Iterator.h>
@ -39,10 +40,16 @@ EdgeConditionBuilder::EdgeConditionBuilder(AstNode* modCondition)
_toCondition(nullptr),
_modCondition(modCondition),
_containsCondition(false) {
TRI_ASSERT(_modCondition->type == NODE_TYPE_OPERATOR_NARY_AND);
#ifdef TRI_ENABLE_MAINTAINER_MODE
if (_modCondition != nullptr) {
TRI_ASSERT(_modCondition->type == NODE_TYPE_OPERATOR_NARY_AND);
}
#endif
}
void EdgeConditionBuilder::addConditionPart(AstNode const* part) {
TRI_ASSERT(_modCondition != nullptr);
TRI_ASSERT(_modCondition->type == NODE_TYPE_OPERATOR_NARY_AND);
TRI_ASSERT(!_containsCondition);
// The ordering is only maintained before we request a specific
// condition
@ -71,7 +78,7 @@ void EdgeConditionBuilder::swapSides(AstNode* cond) {
TRI_ASSERT(_modCondition->numMembers() > 0);
}
AstNode const* EdgeConditionBuilder::getOutboundCondition() {
AstNode* EdgeConditionBuilder::getOutboundCondition() {
if (_fromCondition == nullptr) {
buildFromCondition();
}
@ -80,7 +87,7 @@ AstNode const* EdgeConditionBuilder::getOutboundCondition() {
return _modCondition;
}
AstNode const* EdgeConditionBuilder::getInboundCondition() {
AstNode* EdgeConditionBuilder::getInboundCondition() {
if (_toCondition == nullptr) {
buildToCondition();
}
@ -89,6 +96,72 @@ AstNode const* EdgeConditionBuilder::getInboundCondition() {
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) {
TRI_ASSERT(arr.isArray());
for (auto const& c : VPackArrayIterator(arr)) {

View File

@ -25,6 +25,7 @@
#define ARANGOD_AQL_GRAPHS_H 1
#include "Basics/Common.h"
#include "Aql/VariableGenerator.h"
namespace arangodb {
@ -82,16 +83,64 @@ class EdgeConditionBuilder {
void addConditionPart(AstNode const*);
// Get the complete condition for outbound edges
AstNode const* getOutboundCondition();
AstNode* getOutboundCondition();
// Get the complete condition for inbound edges
AstNode const* getInboundCondition();
AstNode* getInboundCondition();
private:
// Internal helper to swap _from and _to parts
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 {
public:
explicit Graph(arangodb::velocypack::Slice const&);

View File

@ -22,14 +22,15 @@
////////////////////////////////////////////////////////////////////////////////
#include "RestEdgesHandler.h"
#include "Aql/AstNode.h"
#include "Aql/Graphs.h"
#include "Aql/Variable.h"
#include "Basics/ScopeGuard.h"
#include "Cluster/ClusterMethods.h"
#include "MMFiles/MMFilesEdgeIndex.h"
#include "Utils/CollectionNameResolver.h"
#include "Utils/OperationCursor.h"
#include "Utils/SingleCollectionTransaction.h"
#include "Utils/StandaloneTransactionContext.h"
#include "VocBase/Traverser.h"
#include <velocypack/Iterator.h>
#include <velocypack/velocypack-aliases.h>
@ -47,18 +48,9 @@ RestStatus RestEdgesHandler::execute() {
// execute one of the CRUD methods
switch (type) {
case rest::RequestType::GET: {
case rest::RequestType::GET:
readEdges();
break;
}
case rest::RequestType::PUT:
// Now unsupported. Just temporary to check
TRI_ASSERT(false);
readEdges();
break;
case rest::RequestType::POST:
readEdgesForMultipleVertices();
break;
default:
generateNotImplemented("ILLEGAL " + EDGES_PATH);
break;
@ -68,43 +60,38 @@ RestStatus RestEdgesHandler::execute() {
return RestStatus::DONE;
}
bool RestEdgesHandler::getEdgesForVertexList(
VPackSlice const ids,
TRI_edge_direction_e direction, SingleCollectionTransaction& trx,
VPackBuilder& result, size_t& scannedIndex, size_t& filtered) {
TRI_ASSERT(result.isOpenArray());
TRI_ASSERT(ids.isArray());
trx.orderDitch(trx.cid()); // will throw when it fails
void RestEdgesHandler::readCursor(aql::AstNode* condition,
aql::Variable const* var,
std::string const& collectionName,
SingleCollectionTransaction& trx,
VPackBuilder& result,
std::function<void(DocumentIdentifierToken const&)> cb) {
std::string const collectionName =
trx.resolver()->getCollectionName(trx.cid());
Transaction::IndexHandle indexId = trx.edgeIndexHandle(collectionName);
Transaction::IndexHandle indexId;
bool foundIdx = trx.getBestIndexHandleForFilterCondition(
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;
MMFilesEdgeIndex::buildSearchValueFromArray(direction, ids, searchValueBuilder);
VPackSlice search = searchValueBuilder.slice();
ManagedDocumentResult mmdr;
std::unique_ptr<OperationCursor> cursor(trx.indexScanForCondition(
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()) {
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)) {
}
return true;
}
bool RestEdgesHandler::getEdgesForVertex(
std::string const& id, std::string const& collectionName,
TRI_edge_direction_e direction, SingleCollectionTransaction& trx,
@ -112,30 +99,68 @@ bool RestEdgesHandler::getEdgesForVertex(
TRI_ASSERT(result.isOpenArray());
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;
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)) {
}
// 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) {
if (collection->readDocument(&trx, mmdr, token)) {
result.add(VPackSlice(mmdr.vpack()));
}
scannedIndex++;
};
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;
}
@ -280,160 +305,3 @@ bool RestEdgesHandler::readEdges() {
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;
}

View File

@ -30,8 +30,14 @@
#include <velocypack/Builder.h>
namespace arangodb {
struct DocumentIdentifierToken;
class SingleCollectionTransaction;
namespace aql {
struct AstNode;
struct Variable;
}
class RestEdgesHandler : public RestVocbaseBaseHandler {
public:
explicit RestEdgesHandler(GeneralRequest*, GeneralResponse*);
@ -48,10 +54,14 @@ class RestEdgesHandler : public RestVocbaseBaseHandler {
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
@ -61,15 +71,6 @@ class RestEdgesHandler : public RestVocbaseBaseHandler {
std::string const& id, std::string const& collectionName,
TRI_edge_direction_e direction, SingleCollectionTransaction& trx,
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);
};
}