From 430eb2fd071794c9cf529d64b1d2f4b37a3047d0 Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Tue, 24 Nov 2015 10:23:35 +0100 Subject: [PATCH] First version of CXX RestEdgesHandler. Required for early filtering in Cluster Traversals --- arangod/CMakeLists.txt | 1 + arangod/Makefile.files | 1 + arangod/RestHandler/RestEdgesHandler.cpp | 313 ++++++++++++++++++ arangod/RestHandler/RestEdgesHandler.h | 92 +++++ .../RestHandler/RestVocbaseBaseHandler.cpp | 8 +- arangod/RestHandler/RestVocbaseBaseHandler.h | 6 + arangod/RestServer/ArangoServer.cpp | 5 + 7 files changed, 425 insertions(+), 1 deletion(-) create mode 100644 arangod/RestHandler/RestEdgesHandler.cpp create mode 100644 arangod/RestHandler/RestEdgesHandler.h diff --git a/arangod/CMakeLists.txt b/arangod/CMakeLists.txt index 59b2440f85..4371423546 100644 --- a/arangod/CMakeLists.txt +++ b/arangod/CMakeLists.txt @@ -175,6 +175,7 @@ add_executable( RestHandler/RestCursorHandler.cpp RestHandler/RestDocumentHandler.cpp RestHandler/RestEdgeHandler.cpp + RestHandler/RestEdgesHandler.cpp RestHandler/RestExportHandler.cpp RestHandler/RestImportHandler.cpp RestHandler/RestJobHandler.cpp diff --git a/arangod/Makefile.files b/arangod/Makefile.files index b35658d92a..121068e047 100644 --- a/arangod/Makefile.files +++ b/arangod/Makefile.files @@ -136,6 +136,7 @@ arangod_libarangod_a_SOURCES = \ arangod/RestHandler/RestDebugHelperHandler.cpp \ arangod/RestHandler/RestDocumentHandler.cpp \ arangod/RestHandler/RestEdgeHandler.cpp \ + arangod/RestHandler/RestEdgesHandler.cpp \ arangod/RestHandler/RestExportHandler.cpp \ arangod/RestHandler/RestImportHandler.cpp \ arangod/RestHandler/RestJobHandler.cpp \ diff --git a/arangod/RestHandler/RestEdgesHandler.cpp b/arangod/RestHandler/RestEdgesHandler.cpp new file mode 100644 index 0000000000..27d4351331 --- /dev/null +++ b/arangod/RestHandler/RestEdgesHandler.cpp @@ -0,0 +1,313 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief edges request handler +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 2014 ArangoDB GmbH, Cologne, Germany +/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// +/// Copyright holder is ArangoDB GmbH, Cologne, Germany +/// +/// @author Michael Hackstein +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany +/// @author Copyright 2010-2014, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +#include "RestEdgesHandler.h" +#include "VocBase/Traverser.h" + +using namespace triagens::rest; +using namespace triagens::arango; + +// ----------------------------------------------------------------------------- +// --SECTION-- constructors and destructors +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief constructor +//////////////////////////////////////////////////////////////////////////////// + +RestEdgesHandler::RestEdgesHandler (HttpRequest* request) + : RestVocbaseBaseHandler(request) { +} + + +// ----------------------------------------------------------------------------- +// --SECTION-- Handler methods +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// {@inheritDoc} +//////////////////////////////////////////////////////////////////////////////// + +HttpHandler::status_t RestEdgesHandler::execute () { + // extract the sub-request type + HttpRequest::HttpRequestType type = _request->requestType(); + + // execute one of the CRUD methods + switch (type) { + case HttpRequest::HTTP_REQUEST_GET: readEdges(); break; + // case HttpRequest::HTTP_REQUEST_PUT: readEdges(); break; + + + case HttpRequest::HTTP_REQUEST_PUT: + case HttpRequest::HTTP_REQUEST_PATCH: + case HttpRequest::HTTP_REQUEST_HEAD: + case HttpRequest::HTTP_REQUEST_POST: + case HttpRequest::HTTP_REQUEST_DELETE: + case HttpRequest::HTTP_REQUEST_ILLEGAL: + default: { + generateNotImplemented("ILLEGAL " + EDGES_PATH); + break; + } + } + + // this handler is done + return status_t(HANDLER_DONE); +} + +// ----------------------------------------------------------------------------- +// --SECTION-- protected methods +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock API_EDGE_READINOUTBOUND +/// @brief get edges +/// +/// @RESTHEADER{GET /_api/edges/{collection-id}, Read in- or outbound edges} +/// +/// @RESTURLPARAMETERS +/// +/// @RESTURLPARAM{collection-id,string,required} +/// The id of the collection. +/// +/// @RESTQUERYPARAMETERS +/// +/// @RESTQUERYPARAM{vertex,string,required} +/// The id of the start vertex. +/// +/// @RESTQUERYPARAM{direction,string,optional} +/// Selects *in* or *out* direction for edges. If not set, any edges are +/// returned. +/// +/// @RESTDESCRIPTION +/// Returns an array of edges starting or ending in the vertex identified by +/// *vertex-handle*. +/// +/// @RESTRETURNCODES +/// +/// @RESTRETURNCODE{200} +/// is returned if the edge collection was found and edges were retrieved. +/// +/// @RESTRETURNCODE{400} +/// is returned if the request contains invalid parameters. +/// +/// @RESTRETURNCODE{404} +/// is returned if the edge collection was not found. +/// +/// @EXAMPLES +/// +/// Any direction +/// +/// @EXAMPLE_ARANGOSH_RUN{RestEdgesReadEdgesAny} +/// var Graph = require("org/arangodb/graph-blueprint").Graph; +/// var g = new Graph("graph", "vertices", "edges"); +/// var v1 = g.addVertex(1); +/// var v2 = g.addVertex(2); +/// var v3 = g.addVertex(3); +/// var v4 = g.addVertex(4); +/// g.addEdge(v1, v3, 5, "v1 -> v3"); +/// g.addEdge(v2, v1, 6, "v2 -> v1"); +/// g.addEdge(v4, v1, 7, "v4 -> v1"); +/// +/// var url = "/_api/edges/edges?vertex=vertices/1"; +/// var response = logCurlRequest('GET', url); +/// +/// assert(response.code === 200); +/// +/// logJsonResponse(response); +/// db._drop("edges"); +/// db._drop("vertices"); +/// db._graphs.remove("graph"); +/// @END_EXAMPLE_ARANGOSH_RUN +/// +/// In edges +/// +/// @EXAMPLE_ARANGOSH_RUN{RestEdgesReadEdgesIn} +/// var Graph = require("org/arangodb/graph-blueprint").Graph; +/// var g = new Graph("graph", "vertices", "edges"); +/// var v1 = g.addVertex(1); +/// var v2 = g.addVertex(2); +/// var v3 = g.addVertex(3); +/// var v4 = g.addVertex(4); +/// g.addEdge(v1, v3, 5, "v1 -> v3"); +/// g.addEdge(v2, v1, 6, "v2 -> v1"); +/// g.addEdge(v4, v1, 7, "v4 -> v1"); +/// +/// var url = "/_api/edges/edges?vertex=vertices/1&direction=in"; +/// var response = logCurlRequest('GET', url); +/// +/// assert(response.code === 200); +/// +/// logJsonResponse(response); +/// db._drop("edges"); +/// db._drop("vertices"); +/// db._graphs.remove("graph"); +/// @END_EXAMPLE_ARANGOSH_RUN +/// +/// Out edges +/// +/// @EXAMPLE_ARANGOSH_RUN{RestEdgesReadEdgesOut} +/// var Graph = require("org/arangodb/graph-blueprint").Graph; +/// var g = new Graph("graph", "vertices", "edges"); +/// var v1 = g.addVertex(1); +/// var v2 = g.addVertex(2); +/// var v3 = g.addVertex(3); +/// var v4 = g.addVertex(4); +/// g.addEdge(v1, v3, 5, "v1 -> v3"); +/// g.addEdge(v2, v1, 6, "v2 -> v1"); +/// g.addEdge(v4, v1, 7, "v4 -> v1"); +/// +/// var url = "/_api/edges/edges?vertex=vertices/1&direction=out"; +/// var response = logCurlRequest('GET', url); +/// +/// assert(response.code === 200); +/// +/// logJsonResponse(response); +/// db._drop("edges"); +/// db._drop("vertices"); +/// db._graphs.remove("graph"); +/// @END_EXAMPLE_ARANGOSH_RUN +/// @endDocuBlock +//////////////////////////////////////////////////////////////////////////////// + +bool RestEdgesHandler::readEdges () { + std::vector const& suffix = _request->suffix(); + + if (! (suffix.size() == 1)) { + generateError(HttpResponse::BAD, + TRI_ERROR_HTTP_BAD_PARAMETER, + "expected GET " + EDGES_PATH + + "/?vertex=&direction="); + return false; + } + + std::string collectionName = suffix[0]; + // TODO check if collection exists + + bool found; + char const* dir = _request->value("direction", found); + + if (! found || *dir == '\0') { + dir = "any"; + } + + std::string dirString(dir); + 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(HttpResponse::BAD, + TRI_ERROR_HTTP_BAD_PARAMETER, + " must by any, in, or out, not: " + dirString); + return false; + } + + char const* startVertex = _request->value("vertex", found); + + if (! found || *startVertex == '\0') { + generateError(HttpResponse::BAD, + TRI_ERROR_HTTP_BAD_PARAMETER, + "illegal document handle"); + return false; + } + + if (ServerState::instance()->isCoordinator()) { + // TODO + // return getDocumentCoordinator(collection, key, generateBody); + return false; + } + + // find and load collection given by name or identifier + SingleCollectionReadOnlyTransaction trx(new StandaloneTransactionContext(), _vocbase, collectionName); + + // ............................................................................. + // inside read transaction + // ............................................................................. + + int res = trx.begin(); + if (res != TRI_ERROR_NO_ERROR) { + generateTransactionError(collectionName, res); + return false; + } + + TRI_voc_cid_t const cid = trx.cid(); + + // 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(cid); + } + + triagens::arango::traverser::VertexId start; + try { + start = triagens::arango::traverser::IdStringToVertexId ( + trx.resolver(), + startVertex + ); + } + catch (triagens::basics::Exception& e) { + handleError(e); + return false; + } + + TRI_transaction_collection_t* collection = trx.trxCollection(); + + std::vector&& edges = TRI_LookupEdgesDocumentCollection( + collection->_collection->_collection, + direction, + start.cid, + const_cast(start.key) + ); + + res = trx.finish(res); + if (res != TRI_ERROR_NO_ERROR) { + generateTransactionError(collectionName, res); + return false; + } + + + // generate result + triagens::basics::Json documents(triagens::basics::Json::Array); + documents.reserve(edges.size()); + + for (auto& e : edges) { + DocumentAccessor da(trx.resolver(), collection->_collection->_collection, &e); + documents.add(da.toJson()); + } + + return true; +} + diff --git a/arangod/RestHandler/RestEdgesHandler.h b/arangod/RestHandler/RestEdgesHandler.h new file mode 100644 index 0000000000..475b177c68 --- /dev/null +++ b/arangod/RestHandler/RestEdgesHandler.h @@ -0,0 +1,92 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief edges request handler +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 2014 ArangoDB GmbH, Cologne, Germany +/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// +/// Copyright holder is ArangoDB GmbH, Cologne, Germany +/// +/// @author Michael Hackstein +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany +/// @author Copyright 2010-2013, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +#ifndef ARANGODB_REST_HANDLER_REST_EDGES_HANDLER_H +#define ARANGODB_REST_HANDLER_REST_EDGES_HANDLER_H 1 + +#include "Basics/Common.h" + +#include "RestHandler/RestVocbaseBaseHandler.h" + +// ----------------------------------------------------------------------------- +// --SECTION-- RestEdgesHandler +// ----------------------------------------------------------------------------- + +namespace triagens { + namespace arango { + +// ----------------------------------------------------------------------------- +// --SECTION-- constructors and destructors +// ----------------------------------------------------------------------------- + + class RestEdgesHandler : public RestVocbaseBaseHandler { + +// ----------------------------------------------------------------------------- +// --SECTION-- constructors and destructors +// ----------------------------------------------------------------------------- + + + public: + +//////////////////////////////////////////////////////////////////////////////// +/// @brief constructor +//////////////////////////////////////////////////////////////////////////////// + + explicit RestEdgesHandler (rest::HttpRequest*); + +// ----------------------------------------------------------------------------- +// --SECTION-- Handler methods +// ----------------------------------------------------------------------------- + + public: + +//////////////////////////////////////////////////////////////////////////////// +/// {@inheritDoc} +//////////////////////////////////////////////////////////////////////////////// + + status_t execute () override final; + +// ----------------------------------------------------------------------------- +// --SECTION-- protected methods +// ----------------------------------------------------------------------------- + + + protected: + +//////////////////////////////////////////////////////////////////////////////// +/// @brief reads all edges in given direction for given vertex +//////////////////////////////////////////////////////////////////////////////// + + bool readEdges (); + }; + + } +} + +#endif diff --git a/arangod/RestHandler/RestVocbaseBaseHandler.cpp b/arangod/RestHandler/RestVocbaseBaseHandler.cpp index dd982c9a55..1dc24534b2 100644 --- a/arangod/RestHandler/RestVocbaseBaseHandler.cpp +++ b/arangod/RestHandler/RestVocbaseBaseHandler.cpp @@ -71,11 +71,17 @@ const string RestVocbaseBaseHandler::CURSOR_PATH = "/_api/cursor"; const string RestVocbaseBaseHandler::DOCUMENT_PATH = "/_api/document"; //////////////////////////////////////////////////////////////////////////////// -/// @brief document path +/// @brief edge path //////////////////////////////////////////////////////////////////////////////// const string RestVocbaseBaseHandler::EDGE_PATH = "/_api/edge"; +//////////////////////////////////////////////////////////////////////////////// +/// @brief edges path +//////////////////////////////////////////////////////////////////////////////// + +const string RestVocbaseBaseHandler::EDGES_PATH = "/_api/edges"; + //////////////////////////////////////////////////////////////////////////////// /// @brief export path //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/RestHandler/RestVocbaseBaseHandler.h b/arangod/RestHandler/RestVocbaseBaseHandler.h index bbfe259109..e146c5a09b 100644 --- a/arangod/RestHandler/RestVocbaseBaseHandler.h +++ b/arangod/RestHandler/RestVocbaseBaseHandler.h @@ -97,6 +97,12 @@ namespace triagens { static const std::string EDGE_PATH; +//////////////////////////////////////////////////////////////////////////////// +/// @brief edges path +//////////////////////////////////////////////////////////////////////////////// + + static const std::string EDGES_PATH; + //////////////////////////////////////////////////////////////////////////////// /// @brief document export path //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/RestServer/ArangoServer.cpp b/arangod/RestServer/ArangoServer.cpp index 629b420243..91e0b654c2 100644 --- a/arangod/RestServer/ArangoServer.cpp +++ b/arangod/RestServer/ArangoServer.cpp @@ -67,6 +67,7 @@ #include "RestHandler/RestDebugHelperHandler.h" #include "RestHandler/RestDocumentHandler.h" #include "RestHandler/RestEdgeHandler.h" +#include "RestHandler/RestEdgesHandler.h" #include "RestHandler/RestExportHandler.h" #include "RestHandler/RestHandlerCreator.h" #include "RestHandler/RestImportHandler.h" @@ -152,6 +153,10 @@ void ArangoServer::defineHandlers (HttpHandlerFactory* factory) { // add "/edge" handler factory->addPrefixHandler(RestVocbaseBaseHandler::EDGE_PATH, RestHandlerCreator::createNoData); + + // add "/edges" handler + factory->addPrefixHandler(RestVocbaseBaseHandler::EDGES_PATH, + RestHandlerCreator::createNoData); // add "/export" handler factory->addPrefixHandler(RestVocbaseBaseHandler::EXPORT_PATH,