1
0
Fork 0

Gharial rewrite in C++ (#5631)

* Built a C++ skeleton REST handler for gharial, with fallback to the JS handler

* Moved aql::Graph to graph::Graph

* Added complete edge definitions to Graph

Also:
- some cleanup
- used forward-declarations in headers
- use Graph in graph rest handler

* Handle graph lookup failures according to the test suite

* Added GET vertex

* Bugfixes in ResultT

- Added missing #include
- Fixed move semantics

* Move central code of readVertex to GraphOperations

* ResultT fixes and complements

* Implemented a graph cache

* Added and used graph cache to the rest handler

* Added GET edge

* Added DELETE edge

* Extracted some code

* Added PATCH and PUT for both edge and vertex

* Moved update/replace transaction code to GraphOperations

* Added stub routes for POST and a TODO note

* Added a test checking that deleting a vertex removes all incident edges as well

* Added a test checking that deleting a vertex does not remove edges in non-graph collections

* fixed compiler warnings and errors

* Began work on DELETE vertex

For this, added a V8Context to allow for AQL queries to use subtransactions

* Continued work on DELETE vertex (still WIP)

* prep for graph post routes

* fixed removeVertex operation (aql)

* added post vertex and post edge gharial routes

* wasSynchronoues flag changed

* gharial post c++ handler, naming conventions

* added gharial tests

* temporary disabled cache (because not completed), added graph property read functions

* added c++ gharial list vertex collections

* added c++ gharial graph config

* added c++ gharial list graphs

* added graph manager class

* first implementation of create graph in c++, WIP

* changed error messages

* added etag to create graph api, still multiple edge definition check missing

* finished POST /_api/gharial/<graph>

* WIP - DELETE /_api/gharial/<graphName>

* added DELETE /_api/gharial/<graphName> validation, still missing correct response

* gharial delete

* fixed delete gharial lock

* finished DELETE /_api/gharial/<graphName>

* added routes for graph based vertices and edge definitions

* improved delete route

* added add new edge definition to existing graph

* patch edge definition in a graph, still <WIP>

* finished edit edge definition route

* code changes due to devel code changes

* added remove edge definition route

* added vertex delete function

* added todo note regarding one drop collection issue

* add oprhan collection to graph route implemented

* Added a test

* Updated a comment

* Several minor changes

* Minor changes during review

* Changes during review

* Changes during review

* Bugfix: orphans may be null or omitted

* Bugfix: resolve externals

* minor code changes

* seperated graph class to independent component classes

* seperated graph class to independent component classes

* removed log output

* fixed create collection behaviour in a cluster environment

* fixed enterprise graphs behaviour in c++ gharial api

* removed log output

* formatting

* improved error handling, fixed a linux compile bug

* more result refactoring

* more result type cleanup

* fixed wrongly defined test

* result handling

* error handling

* more refactoring

* Bugfix: avoid race condition in cluster when creating collections

* updated graph documentation

* added graph related static strings

* static strings, new method to create options for gharial created collections

* Some minor cleanup

* more use of static strings

* minor code changes, review

* added missing parseint

* removed gharial foxx, added js common module, added v8 general graph module

* correct use of virtual method

* more v8, js general graph, broken state

* more v8 graph functions

* fixed editEdgeDefinition, added drop function

* fixed drop behaviour

* added _list, _exists

* added c++ rename graph collections, added v8 + graph module function

* Added a regression test

* added graph._deleteEdgeDefinition, v8, server

* more v8g

* added _removeVertexCollection

* added _extendEditDefinitions

* todo, need to add a helper sort method for a local defined relation

* fixed test

* fixed lots of tests, added more client functions, _addVertexCollection on client module is still broken

* added more client graph functions, all tests green

* more client functions

* add del edge def route

* Fix use after move

* Minor changes in client general-graph.js module

* Make a copy before sorting (don't touch the argument)

* Minor changes and some additional asserts in graph tests

* Consistently set parameter defaults

* Renamed static strings

* Remove superfluous function

* Made comment more verbose

* Minor changes in general-graph-common.js

* Added missing template arguments

* Fixed community build

* Cleanup in editEdgeDefinition

* Regression test & bugfix: comparison of edge definitions didn' order from and to

* Fixed errors introduced by merge

* Minor changes in v8-general-graph.cpp

* Fixed test failure due to wrong error code in CE

* added missing id field

* Added permission checks for graph._create

* Removed assertion that is no longer valid

* Moved removeGraph from GraphOperations to GraphManager

* Allow C++ implementation of graph._drop to handle smart graphs

* Flush js client db cache after creating/dropping collections via the general graph module

* Added _deleteEdgeDefinition to the general graph client module

* WIP: Added permission checks for drop graph

* Fixed permission checks for drop graph

* Added permission checks for other graph operations

* Bugfix: assert edge definitions are returned in order

* Some cleanup

* Removed unused method

* Minor improvements in GraphManager

* Fixed a type in general-graph common module

* Most useful fix of all times ever: Do not auto cast from bool to int and alternate error/noerror by this

* Added the initial keyword to StaticStrings

* Added a new error code, used whenever a user tries to inject a documentcollection as a relation into the graph, which is invalid

* Some GraphManager/Ops/Graph cleanup. Less Slice parsing, more usage of GraphObjects

* Test edgeDefinitions in graphs with a defined ordering

* GraphClass Layout cleanup

* Do not test error messages, use codes instead

* Recreated backwards compatibility of Graph Creation Permission errors

* Changed error-code if edgedefinition is used twice

* Added a StaticString for the GraphName

* Renamed graphToVpack => graphForClient

* Partly fixed graph-api test to work with better error messages. Still red: The edgeDefinitions are now sorted, the test is supposed to sort his own list, but appearently does not do so. Under investigation

* Added a new error code that rejects injection of differently sharded smart collection into smartgraph. Should be more helpful to our users

* graph createCollectionOptions now require an open object to be cross-called from enterprise. Made enterprise switch for creation of graph more elegant.

* Updated graphs.cpp

* Massive refactoring. Made Factories for graphs to make SmartGraph much more transparent. Also reduced amount of multiple implementations of the same stuff. Killed vocbase/graphs use GraphManager instead. Removed usage of GraphCache, was not completely implemented anyway and only partially used, which is bad at the moment. Option for later improvement never the less

* Adapted JS code to now really use c++ variants. ALso included 3 Classes: Graph, SmartGraph and GraphModule.

* Fixed undefined behaviour in Remove Vertex. Fixed smartgraph sharding if one collection already exists.

* Removed DEBUG output

* Removed DEBUG logs

* Removed dead code

* Fixed Graph EdgeDefinition test, they now have a different ordering.

* Added a test when adding a vertexCollection that it is actually valid in the graph

* Client Graph API now correctly sends `orphanCollections` and not `orphans`

* Let GraphOperations modify the graph in-place. It should now properly handle edgeDefinitions.

* Added initial cid StaticString

* Included the vocbase in fromPersistence creation of Graphs. Only required to enhance 3.3 SmartGraphs on the fly.

* Fixed internal error message

* Fixed compiler isses originiated from merging

* Removed unused imports

* Regenerated generated file
This commit is contained in:
Tobias Gödderz 2018-08-09 09:30:04 +02:00 committed by Michael Hackstein
parent 4aa174399c
commit de4f5587ae
63 changed files with 7807 additions and 1939 deletions

View File

@ -384,7 +384,7 @@ graph with different *from* and/or *to* collections an error is thrown.
Modify an relation definition
`graph_module._editEdgeDefinition(edgeDefinition)`
`graph_module._editEdgeDefinitions(edgeDefinition)`
Edits one relation definition of a graph. The edge definition used as argument will
replace the existing edge definition of the graph which has the same collection.

View File

@ -23,6 +23,10 @@ The *_key* attribute of the vertex.
@RESTQUERYPARAM{waitForSync,boolean,optional}
Define if the request should wait until synced to disk.
@RESTQUERYPARAM{returnOld,boolean,optional}
Define if a presentation of the deleted document should
be returned within the response object.
@RESTHEADERPARAMETERS
@RESTHEADERPARAM{if-match,string,optional}

View File

@ -26,6 +26,16 @@ Define if the request should wait until synced to disk.
@RESTQUERYPARAM{keepNull,boolean,optional}
Define if values set to null should be stored. By default the key is not removed from the document.
@RESTQUERYPARAM{returnOld,boolean,optional}
Define if a presentation of the deleted document should
be returned within the response object.
@RESTQUERYPARAM{returnNew,boolean,optional}
Define if a presentation of the newly create
@RESTQUERYPARAM{keepNull,boolean,optional}
Define if values set to null should be stored. By default the key is not removed from the document.
@RESTALLBODYPARAM{updateAttributes,object,required}
The body has to be a JSON object containing the attributes to be updated.

View File

@ -22,6 +22,16 @@ The *_key* attribute of the vertex.
@RESTQUERYPARAM{waitForSync,boolean,optional}
Define if the request should wait until synced to disk.
@RESTQUERYPARAM{keepNull,boolean,optional}
Define if values set to null should be stored. By default the key is not removed from the document.
@RESTQUERYPARAM{returnOld,boolean,optional}
Define if a presentation of the deleted document should
be returned within the response object.
@RESTQUERYPARAM{returnNew,boolean,optional}
Define if a presentation of the newly create
@RESTHEADERPARAMETERS
@RESTHEADERPARAM{if-match,string,optional}

View File

@ -23,6 +23,14 @@ The *_key* attribute of the vertex.
@RESTQUERYPARAM{waitForSync,boolean,optional}
Define if the request should wait until synced to disk.
@RESTQUERYPARAM{returnOld,boolean,optional}
Define if a presentation of the deleted document should
be returned within the response object.
@RESTQUERYPARAM{returnNew,boolean,optional}
Define if a presentation of the newly created document
should be returned within the response object.
@RESTHEADERPARAMETERS
@RESTHEADERPARAM{if-match,string,optional}

View File

@ -26,6 +26,13 @@ Define if the request should wait until synced to disk.
@RESTQUERYPARAM{keepNull,boolean,optional}
Define if values set to null should be stored. By default the key is not removed from the document.
@RESTQUERYPARAM{returnOld,boolean,optional}
Define if a presentation of the deleted document should
be returned within the response object.
@RESTQUERYPARAM{returnNew,boolean,optional}
Define if a presentation of the newly create
@RESTHEADERPARAMETERS
@RESTHEADERPARAM{if-match,string,optional}

View File

@ -23,6 +23,16 @@ The *_key* attribute of the vertex.
@RESTQUERYPARAM{waitForSync,boolean,optional}
Define if the request should wait until synced to disk.
@RESTQUERYPARAM{keepNull,boolean,optional}
Define if values set to null should be stored. By default the key is not removed from the document.
@RESTQUERYPARAM{returnOld,boolean,optional}
Define if a presentation of the deleted document should
be returned within the response object.
@RESTQUERYPARAM{returnNew,boolean,optional}
Define if a presentation of the newly create
@RESTHEADERPARAMETERS
@RESTHEADERPARAM{if-match,string,optional}

View File

@ -159,7 +159,6 @@ describe ArangoDB do
return doc
end
def create_edge (waitForSync, graph_name, collection, from, to, body, options = {})
cmd = edge_endpoint(graph_name, collection)
cmd = cmd + "?waitForSync=#{waitForSync}"
@ -282,6 +281,7 @@ describe ArangoDB do
second_def = { "collection" => bought_collection, "from" => [user_collection], "to" => [product_collection] }
doc = additional_edge_definition(sync, graph_name, second_def )
edge_definition.push(second_def)
edge_definition = edge_definition.sort_by { |d| [ -d["collection"] ] }
doc.code.should eq(202)
doc.parsed_response['error'].should eq(false)
@ -848,7 +848,10 @@ describe ArangoDB do
it "can not replace a non existing edge" do
key = "unknownKey"
doc = replace_edge( sync, graph_name, friend_collection, key, {"type2" => "divorced"})
# Added _from and _to, because otherwise a 400 might conceal the
# 404. Another test checking that missing _from or _to trigger
# errors was added to api-gharial-spec.js.
doc = replace_edge( sync, graph_name, friend_collection, key, {"type2" => "divorced", "_from" => "1", "_to" => "2"})
doc.code.should eq(404)
doc.parsed_response['error'].should eq(true)
doc.parsed_response['errorMessage'].should include("document not found")
@ -1169,11 +1172,18 @@ describe ArangoDB do
doc.parsed_response['code'].should eq(404)
end
def check400 (doc)
doc.code.should eq(400)
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
puts doc.parsed_response['errorMessage']
doc.parsed_response['errorMessage'].should include("edge attribute missing or invalid")
end
def check404Edge (doc)
check404(doc)
doc.parsed_response['errorNum'].should eq(1930)
doc.parsed_response['errorMessage'].should eq("edge collection not used in graph")
end
def check404Vertex (doc)
@ -1181,10 +1191,23 @@ describe ArangoDB do
doc.parsed_response['errorNum'].should eq(1926)
end
def check400VertexUnused (doc)
doc.parsed_response['errorNum'].should eq(1928)
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
puts doc.parsed_response['errorMessage']
doc.parsed_response['errorMessage'].should include("not in orphan collection")
end
def check404CRUD (doc)
check404(doc)
doc.parsed_response['errorNum'].should eq(1203)
doc.parsed_response['errorMessage'].should eq("collection or view not found")
doc.parsed_response['errorMessage'].should start_with("collection or view not found: ")
end
def check400CRUD (doc)
check400(doc)
doc.parsed_response['errorNum'].should eq(1233)
end
it "change edge definition" do
@ -1197,7 +1220,8 @@ describe ArangoDB do
end
it "delete vertex collection" do
check404Vertex(delete_vertex_collection( sync, graph_name, unknown_name))
# this checks if a not used vertex collection can be removed of a graph
check400VertexUnused(delete_vertex_collection( sync, graph_name, unknown_name))
end
it "create vertex" do
@ -1208,6 +1232,8 @@ describe ArangoDB do
check404CRUD(get_vertex(graph_name, unknown_name, unknown_name))
end
# TODO add tests where the edge/vertex collection is not part of the graph, but
# the given key exists!
it "update vertex" do
check404CRUD(update_vertex( sync, graph_name, unknown_name, unknown_name, {}))
end
@ -1221,7 +1247,7 @@ describe ArangoDB do
end
it "create edge" do
check404CRUD(create_edge( sync, graph_name, unknown_name, unknown_name, unknown_name, {}))
check400CRUD(create_edge( sync, graph_name, unknown_name, unknown_name, unknown_name, {}))
end
it "get edge" do
@ -1277,7 +1303,10 @@ describe ArangoDB do
end
it "replace edge" do
check404(replace_edge( sync, graph_name, friend_collection, unknown_name, {}))
# Added _from and _to, because otherwise a 400 might conceal the
# 404. Another test checking that missing _from or _to trigger
# errors was added to api-gharial-spec.js.
check404(replace_edge( sync, graph_name, friend_collection, unknown_name, {"_from" => "1", "_to" => "2"}))
end
it "delete edge" do

View File

@ -35,6 +35,7 @@
#include "Basics/StringUtils.h"
#include "Basics/tri-strings.h"
#include "Cluster/ClusterInfo.h"
#include "Graph/Graph.h"
#include "Transaction/Helpers.h"
#include "Utils/CollectionNameResolver.h"
#include "VocBase/LogicalCollection.h"

View File

@ -32,6 +32,7 @@
#include "Aql/Query.h"
#include "Cluster/ServerState.h"
#include "Graph/BaseOptions.h"
#include "Graph/Graph.h"
#include "Utils/CollectionNameResolver.h"
#include "VocBase/LogicalCollection.h"

View File

@ -32,12 +32,11 @@ namespace arangodb {
namespace graph {
struct BaseOptions;
class Graph;
}
namespace aql {
class Graph;
// @brief This is a pure virtual super-class for all AQL graph operations
// It does the generally required:
// * graph info parsing
@ -145,7 +144,7 @@ class GraphNode : public ExecutionNode {
Variable const* _edgeOutVariable;
/// @brief our graph...
Graph const* _graphObj;
graph::Graph const* _graphObj;
/// @brief Temporary pseudo variable for the currently traversed object.
Variable const* _tmpObjVariable;

View File

@ -21,20 +21,17 @@
/// @author Michael Hackstein
////////////////////////////////////////////////////////////////////////////////
#include <velocypack/Iterator.h>
#include "Graphs.h"
#include "Aql/AstNode.h"
#include "Basics/StaticStrings.h"
#include "Basics/VelocyPackHelper.h"
#include <velocypack/Iterator.h>
#include <velocypack/velocypack-aliases.h>
#include "Graph/Graph.h"
using namespace arangodb::basics;
using namespace arangodb::aql;
char const* Graph::_attrEdgeDefs = "edgeDefinitions";
char const* Graph::_attrOrphans = "orphanCollections";
EdgeConditionBuilder::EdgeConditionBuilder(AstNode* modCondition)
: _fromCondition(nullptr),
_toCondition(nullptr),
@ -161,83 +158,3 @@ 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)) {
TRI_ASSERT(c.isString());
addVertexCollection(c.copyString());
}
}
std::unordered_set<std::string> const& Graph::vertexCollections() const {
return _vertexColls;
}
std::unordered_set<std::string> const& Graph::edgeCollections() const {
return _edgeColls;
}
void Graph::addEdgeCollection(std::string const& name) {
_edgeColls.insert(name);
}
void Graph::addVertexCollection(std::string const& name) {
_vertexColls.insert(name);
}
void Graph::toVelocyPack(VPackBuilder& builder) const {
VPackObjectBuilder guard(&builder);
if (!_vertexColls.empty()) {
builder.add(VPackValue("vertexCollectionNames"));
VPackArrayBuilder guard2(&builder);
for (auto const& cn : _vertexColls) {
builder.add(VPackValue(cn));
}
}
if (!_edgeColls.empty()) {
builder.add(VPackValue("edgeCollectionNames"));
VPackArrayBuilder guard2(&builder);
for (auto const& cn : _edgeColls) {
builder.add(VPackValue(cn));
}
}
}
Graph::Graph(VPackSlice const& slice) : _vertexColls(), _edgeColls() {
if (slice.hasKey(_attrEdgeDefs)) {
auto edgeDefs = slice.get(_attrEdgeDefs);
for (auto const& def : VPackArrayIterator(edgeDefs)) {
TRI_ASSERT(def.isObject());
try {
std::string eCol = arangodb::basics::VelocyPackHelper::getStringValue(
def, "collection", "");
addEdgeCollection(eCol);
} catch (...) {
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_GRAPH_INVALID_GRAPH, "didn't find 'collection' in the graph definition");
}
// TODO what if graph is not in a valid format any more
try {
VPackSlice tmp = def.get("from");
insertVertexCollections(tmp);
} catch (...) {
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_GRAPH_INVALID_GRAPH, "didn't find from-collection in the graph definition");
}
try {
VPackSlice tmp = def.get("to");
insertVertexCollections(tmp);
} catch (...) {
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_GRAPH_INVALID_GRAPH, "didn't find to-collection in the graph definition");
}
}
}
if (slice.hasKey(_attrOrphans)) {
auto orphans = slice.get(_attrOrphans);
insertVertexCollections(orphans);
}
}
void Graph::enhanceEngineInfo(VPackBuilder&) const {
}

View File

@ -24,8 +24,8 @@
#ifndef ARANGOD_AQL_GRAPHS_H
#define ARANGOD_AQL_GRAPHS_H 1
#include "Basics/Common.h"
#include "Aql/VariableGenerator.h"
#include "Basics/Common.h"
namespace arangodb {
@ -141,51 +141,6 @@ class EdgeConditionBuilderContainer final : public EdgeConditionBuilder {
VariableGenerator _varGen;
};
class Graph {
public:
explicit Graph(arangodb::velocypack::Slice const&);
virtual ~Graph() {}
private:
/// @brief the cids of all vertexCollections
std::unordered_set<std::string> _vertexColls;
/// @brief the cids of all edgeCollections
std::unordered_set<std::string> _edgeColls;
/// @brief Graph collection edge definition attribute name
static char const* _attrEdgeDefs;
/// @brief Graph collection orphan list arribute name
static char const* _attrOrphans;
public:
/// @brief Graph collection name
static std::string const _graphs;
/// @brief Add Collections to the object
void insertVertexCollections(arangodb::velocypack::Slice& arr);
public:
/// @brief get the cids of all vertexCollections
std::unordered_set<std::string> const& vertexCollections() const;
/// @brief get the cids of all edgeCollections
std::unordered_set<std::string> const& edgeCollections() const;
/// @brief Add an edge collection to this graphs definition
void addEdgeCollection(std::string const&);
/// @brief Add a vertex collection to this graphs definition
void addVertexCollection(std::string const&);
/// @brief return a VelocyPack representation of the graph
void toVelocyPack(arangodb::velocypack::Builder&) const;
virtual void enhanceEngineInfo(arangodb::velocypack::Builder&) const;
};
} // namespace aql
} // namespace arangodb

View File

@ -39,6 +39,8 @@
#include "Basics/fasthash.h"
#include "Cluster/ServerState.h"
#include "GeneralServer/AuthenticationFeature.h"
#include "Graph/Graph.h"
#include "Graph/GraphManager.h"
#include "Logger/Logger.h"
#include "RestServer/AqlFeature.h"
#include "StorageEngine/TransactionState.h"
@ -49,12 +51,9 @@
#include "V8/v8-conv.h"
#include "V8/v8-vpack.h"
#include "V8Server/V8DealerFeature.h"
#include "VocBase/Graphs.h"
#include "VocBase/vocbase.h"
#include <velocypack/Builder.h>
#include <velocypack/Iterator.h>
#include <velocypack/velocypack-aliases.h>
#ifndef USE_PLAN_CACHE
#undef USE_PLAN_CACHE
@ -1444,24 +1443,24 @@ std::shared_ptr<transaction::Context> Query::createTransactionContext() {
/// @brief look up a graph either from our cache list or from the _graphs
/// collection
Graph const* Query::lookupGraphByName(std::string const& name) {
graph::Graph const* Query::lookupGraphByName(std::string const& name) {
auto it = _graphs.find(name);
if (it == _graphs.end()) {
std::unique_ptr<arangodb::aql::Graph> g(
arangodb::lookupGraphByName(createTransactionContext(), name));
if (g == nullptr) {
return nullptr;
}
auto result = _graphs.emplace(name, std::move(g));
TRI_ASSERT(result.second);
it = result.first;
if (it != _graphs.end()) {
return it->second.get();
}
TRI_ASSERT((*it).second != nullptr);
return (*it).second.get();
graph::GraphManager graphManager{_vocbase, _contextOwnedByExterior};
auto g = graphManager.lookupGraphByName(name);
if (g.fail()) {
return nullptr;
}
auto graph = g.get().get();
_graphs.emplace(name, std::move(g.get()));
return graph;
}
/// @brief returns the next query id

View File

@ -58,6 +58,10 @@ namespace velocypack {
class Builder;
}
namespace graph {
class Graph;
}
namespace aql {
struct AstNode;
@ -280,7 +284,7 @@ class Query {
std::string getStateString() const;
/// @brief look up a graph in the _graphs collection
Graph const* lookupGraphByName(std::string const& name);
graph::Graph const* lookupGraphByName(std::string const& name);
/// @brief return the bind parameters as passed by the user
std::shared_ptr<arangodb::velocypack::Builder> bindParameters() const {
@ -355,7 +359,7 @@ class Query {
V8Context* _context;
/// @brief graphs used in query, identified by name
std::unordered_map<std::string, std::unique_ptr<Graph>> _graphs;
std::unordered_map<std::string, std::unique_ptr<graph::Graph>> _graphs;
/// @brief the actual query string
QueryString _queryString;

View File

@ -556,7 +556,7 @@ auth::Level auth::User::collectionAuthLevel(std::string const& dbname,
return auth::Level::NONE; // invalid collection names
}
// we must have got a non-empty collection name when we get here
TRI_ASSERT(cname[0] < '0' || cname[0] > '9');
TRI_ASSERT(!isdigit(cname[0]));
bool isSystem = cname[0] == '_';
if (isSystem) {

View File

@ -738,8 +738,9 @@ auth::Level auth::UserManager::collectionAuthLevel(std::string const& user,
}
auth::Level level;
if (coll[0] >= '0' && coll[0] <= '9') {
std::string tmpColl = DatabaseFeature::DATABASE->translateCollectionName(dbname, coll);
if (isdigit(coll[0])) {
std::string tmpColl =
DatabaseFeature::DATABASE->translateCollectionName(dbname, coll);
level = it->second.collectionAuthLevel(dbname, tmpColl);
} else {
level = it->second.collectionAuthLevel(dbname, coll);

View File

@ -323,6 +323,9 @@ SET(ARANGOD_SOURCES
Graph/ConstantWeightShortestPathFinder.cpp
Graph/ClusterTraverserCache.cpp
Graph/EdgeCollectionInfo.cpp
Graph/Graph.cpp
Graph/GraphManager.cpp
Graph/GraphOperations.cpp
Graph/NeighborsEnumerator.cpp
Graph/PathEnumerator.cpp
Graph/ShortestPathOptions.cpp
@ -404,6 +407,7 @@ SET(ARANGOD_SOURCES
RestHandler/RestEndpointHandler.cpp
RestHandler/RestEngineHandler.cpp
RestHandler/RestExplainHandler.cpp
RestHandler/RestGraphHandler.cpp
RestHandler/RestImportHandler.cpp
RestHandler/RestIndexHandler.cpp
RestHandler/RestJobHandler.cpp
@ -507,6 +511,7 @@ SET(ARANGOD_SOURCES
V8Server/v8-vocbase.cpp
V8Server/v8-voccursor.cpp
V8Server/v8-vocindex.cpp
V8Server/v8-general-graph.cpp
VocBase/Methods/AqlUserFunctions.cpp
VocBase/Methods/Collections.cpp
VocBase/Methods/Databases.cpp
@ -516,7 +521,6 @@ SET(ARANGOD_SOURCES
VocBase/Methods/Upgrade.cpp
VocBase/Methods/UpgradeTasks.cpp
VocBase/Methods/Version.cpp
VocBase/Graphs.cpp
VocBase/KeyGenerator.cpp
VocBase/LogicalCollection.cpp
VocBase/LogicalDataSource.cpp

View File

@ -26,6 +26,7 @@
#include <boost/optional.hpp>
#include "Basics/Common.h"
#include "Basics/Result.h"
namespace arangodb {
@ -57,7 +58,13 @@ namespace arangodb {
template <typename T>
class ResultT : public arangodb::Result {
public:
ResultT static success(T val) { return ResultT(val, TRI_ERROR_NO_ERROR); }
ResultT static success(T const& val) {
return ResultT(val, TRI_ERROR_NO_ERROR);
}
ResultT static success(T&& val) {
return ResultT(std::move(val), TRI_ERROR_NO_ERROR);
}
ResultT static error(int errorNumber) {
return ResultT(boost::none, errorNumber);
@ -67,17 +74,23 @@ class ResultT : public arangodb::Result {
return ResultT(boost::none, errorNumber, errorMessage);
}
// This is not explicit on purpose
// NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions)
// These are not explicit on purpose
ResultT(Result const& other) : Result(other) {
// .ok() is not allowed here, as _val should be expected to be initialized
// iff .ok() is true.
TRI_ASSERT(other.fail());
}
// This is not explicit on purpose
// NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions)
ResultT(T&& val) : ResultT(std::forward<T>(val), TRI_ERROR_NO_ERROR) {}
ResultT(Result&& other) : Result(std::move(other)) {
// .ok() is not allowed here, as _val should be expected to be initialized
// iff .ok() is true.
TRI_ASSERT(other.fail());
}
// These are not explicit on purpose
ResultT(T&& val) : ResultT(std::move(val), TRI_ERROR_NO_ERROR) {}
ResultT(T const& val) : ResultT(val, TRI_ERROR_NO_ERROR) {}
ResultT() = delete;
@ -91,6 +104,8 @@ class ResultT : public arangodb::Result {
return *this;
}
Result copy_result() const { return *this; }
// These would be very convenient, but also make it very easy to accidentally
// use the value of an error-result. So don't add them.
//
@ -139,11 +154,20 @@ class ResultT : public arangodb::Result {
boost::optional<T> _val;
ResultT(boost::optional<T>&& val_, int errorNumber)
: Result(errorNumber), _val(val_) {}
: Result(errorNumber), _val(std::move(val_)) {}
ResultT(boost::optional<T>&& val_, int errorNumber,
std::string const& errorMessage)
: Result(errorNumber, errorMessage), _val(val_) {}
: Result(errorNumber, errorMessage),
_val(val_) {}
ResultT(boost::optional<T>const& val_, int errorNumber)
: Result(errorNumber), _val(std::move(val_)) {}
ResultT(boost::optional<T>const& val_, int errorNumber,
std::string const& errorMessage)
: Result(errorNumber, errorMessage),
_val(val_) {}
};
} // namespace arangodb

View File

@ -38,6 +38,7 @@
#include "GeneralServer/AuthenticationFeature.h"
#include "GeneralServer/GeneralServer.h"
#include "GeneralServer/RestHandlerFactory.h"
#include "Graph/Graph.h"
#include "InternalRestHandler/InternalRestTraverserHandler.h"
#include "ProgramOptions/Parameters.h"
#include "ProgramOptions/ProgramOptions.h"
@ -60,6 +61,7 @@
#include "RestHandler/RestEndpointHandler.h"
#include "RestHandler/RestEngineHandler.h"
#include "RestHandler/RestExplainHandler.h"
#include "RestHandler/RestGraphHandler.h"
#include "RestHandler/RestHandlerCreator.h"
#include "RestHandler/RestImportHandler.h"
#include "RestHandler/RestIndexHandler.h"
@ -68,9 +70,9 @@
#include "RestHandler/RestPregelHandler.h"
#include "RestHandler/RestQueryCacheHandler.h"
#include "RestHandler/RestQueryHandler.h"
#include "RestHandler/RestRepairHandler.h"
#include "RestHandler/RestShutdownHandler.h"
#include "RestHandler/RestSimpleHandler.h"
#include "RestHandler/RestRepairHandler.h"
#include "RestHandler/RestSimpleQueryHandler.h"
#include "RestHandler/RestStatusHandler.h"
#include "RestHandler/RestTasksHandler.h"
@ -335,6 +337,10 @@ void GeneralServerFeature::defineHandlers() {
RestVocbaseBaseHandler::EDGES_PATH,
RestHandlerCreator<RestEdgesHandler>::createNoData);
_handlerFactory->addPrefixHandler(
RestVocbaseBaseHandler::GHARIAL_PATH,
RestHandlerCreator<RestGraphHandler>::createNoData);
_handlerFactory->addPrefixHandler(
RestVocbaseBaseHandler::ENDPOINT_PATH,
RestHandlerCreator<RestEndpointHandler>::createNoData);

View File

@ -84,7 +84,7 @@ class GeneralServerFeature final
return GENERAL_SERVER->_accessControlAllowOrigins;
}
private:
static GeneralServerFeature* GENERAL_SERVER;
@ -98,7 +98,7 @@ class GeneralServerFeature final
void start() override final;
void stop() override final;
void unprepare() override final;
private:
double _keepAliveTimeout = 300.0;
bool _allowMethodOverride;
@ -118,7 +118,9 @@ class GeneralServerFeature final
private:
std::unique_ptr<rest::RestHandlerFactory> _handlerFactory;
std::unique_ptr<rest::AsyncJobManager> _jobManager;
std::unique_ptr<std::pair<aql::QueryRegistry*, traverser::TraverserEngineRegistry*>> _combinedRegistries;
std::unique_ptr<
std::pair<aql::QueryRegistry*, traverser::TraverserEngineRegistry*>>
_combinedRegistries;
std::vector<rest::GeneralServer*> _servers;
};
}

View File

@ -34,7 +34,7 @@ namespace arangodb {
class GeneralRequest;
class RequestStatistics;
enum class RestStatus { DONE, WAITING, FAIL};
enum class RestStatus { DONE, WAITING, FAIL };
namespace rest {
class RestHandler : public std::enable_shared_from_this<RestHandler> {

534
arangod/Graph/Graph.cpp Normal file
View File

@ -0,0 +1,534 @@
////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2018 ArangoDB 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 Tobias Gödderz
////////////////////////////////////////////////////////////////////////////////
#include "Graph.h"
#include <velocypack/Buffer.h>
#include <velocypack/Collection.h>
#include <velocypack/Iterator.h>
#include <velocypack/velocypack-aliases.h>
#include <array>
#include <boost/variant.hpp>
#include <utility>
#include "Aql/AstNode.h"
#include "Aql/Graphs.h"
#include "Aql/Query.h"
#include "Basics/ReadLocker.h"
#include "Basics/StaticStrings.h"
#include "Basics/VelocyPackHelper.h"
#include "Basics/WriteLocker.h"
#include "Cluster/ServerState.h"
#include "RestServer/QueryRegistryFeature.h"
#include "Transaction/Methods.h"
#include "Transaction/StandaloneContext.h"
#include "Utils/OperationOptions.h"
#include "Utils/SingleCollectionTransaction.h"
#include "VocBase/LogicalCollection.h"
#include "VocBase/Methods/Collections.h"
using namespace arangodb;
using namespace arangodb::graph;
using UserTransaction = transaction::Methods;
using VelocyPackHelper = basics::VelocyPackHelper;
#ifndef USE_ENTERPRISE
// Factory methods
std::unique_ptr<Graph> Graph::fromPersistence(VPackSlice document, TRI_vocbase_t& vocbase) {
std::unique_ptr<Graph> result{new Graph{document}};
return result;
}
std::unique_ptr<Graph> Graph::fromUserInput(std::string&& name, VPackSlice document, VPackSlice options) {
std::unique_ptr<Graph> result{new Graph{std::move(name), document, options}};
return result;
}
#endif
std::unique_ptr<Graph> Graph::fromUserInput(std::string const& name, VPackSlice document, VPackSlice options) {
return Graph::fromUserInput(std::string{name}, document, options);
}
// From persistence
Graph::Graph(velocypack::Slice const& slice)
: _graphName(VelocyPackHelper::getStringValue(slice, StaticStrings::KeyString, "")),
_vertexColls(),
_edgeColls(),
_numberOfShards(basics::VelocyPackHelper::readNumericValue<uint64_t>(
slice, StaticStrings::NumberOfShards, 1)),
_replicationFactor(basics::VelocyPackHelper::readNumericValue<uint64_t>(
slice, StaticStrings::ReplicationFactor, 1)),
_rev(basics::VelocyPackHelper::getStringValue(
slice, StaticStrings::RevString, "")) {
// If this happens we have a document without an _key Attribute.
TRI_ASSERT(!_graphName.empty());
if (_graphName.empty()) {
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "Persisted graph is invalid. It does not have a _key set. Please contact support.");
}
// If this happens we have a document without an _rev Attribute.
TRI_ASSERT(!_rev.empty());
if (_rev.empty()) {
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "Persisted graph is invalid. It does not have a _rev set. Please contact support.");
}
if (slice.hasKey(StaticStrings::GraphEdgeDefinitions)) {
parseEdgeDefinitions(slice.get(StaticStrings::GraphEdgeDefinitions));
}
if (slice.hasKey(StaticStrings::GraphOrphans)) {
insertOrphanCollections(slice.get(StaticStrings::GraphOrphans));
}
}
// From user input
Graph::Graph(std::string&& graphName, VPackSlice const& info, VPackSlice const& options)
: _graphName(graphName),
_vertexColls(),
_edgeColls(),
_numberOfShards(1),
_replicationFactor(1),
_rev("") {
if (_graphName.empty()) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_GRAPH_CREATE_MISSING_NAME);
}
TRI_ASSERT(_rev.empty());
if (info.hasKey(StaticStrings::GraphEdgeDefinitions)) {
parseEdgeDefinitions(info.get(StaticStrings::GraphEdgeDefinitions));
}
if (info.hasKey(StaticStrings::GraphOrphans)) {
insertOrphanCollections(info.get(StaticStrings::GraphOrphans));
}
if (options.isObject()) {
_numberOfShards = VelocyPackHelper::readNumericValue<uint64_t>(
options, StaticStrings::NumberOfShards, 1);
_replicationFactor = VelocyPackHelper::readNumericValue<uint64_t>(
options, StaticStrings::ReplicationFactor, 1);
}
}
void Graph::parseEdgeDefinitions(VPackSlice edgeDefs) {
TRI_ASSERT(edgeDefs.isArray());
if (!edgeDefs.isArray()) {
THROW_ARANGO_EXCEPTION_MESSAGE(
TRI_ERROR_GRAPH_INVALID_GRAPH,
"'edgeDefinitions' are not an array in the graph definition");
}
for (auto const& def : VPackArrayIterator(edgeDefs)) {
auto edgeDefRes = addEdgeDefinition(def);
if (edgeDefRes.fail()) {
THROW_ARANGO_EXCEPTION(edgeDefRes.copy_result());
}
}
}
void Graph::insertOrphanCollections(VPackSlice const arr) {
TRI_ASSERT(arr.isArray());
for (auto const& c : VPackArrayIterator(arr)) {
TRI_ASSERT(c.isString());
addOrphanCollection(c.copyString());
}
}
std::unordered_set<std::string> const& Graph::vertexCollections() const {
return _vertexColls;
}
std::set<std::string> const& Graph::orphanCollections() const {
return _orphanColls;
}
std::set<std::string> const& Graph::edgeCollections() const {
return _edgeColls;
}
std::map<std::string, EdgeDefinition> const& Graph::edgeDefinitions()
const {
return _edgeDefs;
}
uint64_t Graph::numberOfShards() const { return _numberOfShards; }
uint64_t Graph::replicationFactor() const { return _replicationFactor; }
std::string const Graph::id() const {
return std::string(StaticStrings::GraphCollection + "/" + _graphName);
}
std::string const& Graph::rev() const { return _rev; }
void Graph::addVertexCollection(std::string const& name) {
if (_orphanColls.find(name) != _orphanColls.end()) {
// Promote Orphans to vertices
_orphanColls.erase(name);
}
_vertexColls.emplace(name);
}
Result Graph::addOrphanCollection(std::string&& name) {
if (_vertexColls.find(name) != _vertexColls.end()) {
return TRI_ERROR_GRAPH_COLLECTION_USED_IN_EDGE_DEF;
}
TRI_ASSERT(_orphanColls.find(name) == _orphanColls.end());
_vertexColls.emplace(name);
_orphanColls.emplace(std::move(name));
return TRI_ERROR_NO_ERROR;
}
void Graph::setSmartState(bool state) { _isSmart = state; }
void Graph::setNumberOfShards(uint64_t numberOfShards) {
_numberOfShards = numberOfShards;
}
void Graph::setReplicationFactor(uint64_t replicationFactor) {
_replicationFactor = replicationFactor;
}
void Graph::setRev(std::string&& rev) { _rev = std::move(rev); }
void Graph::toVelocyPack(VPackBuilder& builder) const {
VPackObjectBuilder guard(&builder);
if (!_vertexColls.empty()) {
builder.add(VPackValue("vertexCollectionNames"));
VPackArrayBuilder guard2(&builder);
for (auto const& cn : _vertexColls) {
builder.add(VPackValue(cn));
}
}
if (!_edgeColls.empty()) {
builder.add(VPackValue("edgeCollectionNames"));
VPackArrayBuilder guard2(&builder);
for (auto const& cn : _edgeColls) {
builder.add(VPackValue(cn));
}
}
}
void Graph::toPersistence(VPackBuilder& builder) const {
TRI_ASSERT(builder.isOpenObject());
// The name
builder.add(StaticStrings::KeyString, VPackValue(_graphName));
// Cluster Information
builder.add(StaticStrings::NumberOfShards, VPackValue(_numberOfShards));
builder.add(StaticStrings::ReplicationFactor, VPackValue(_replicationFactor));
builder.add(StaticStrings::GraphIsSmart, VPackValue(isSmart()));
// EdgeDefinitions
builder.add(VPackValue(StaticStrings::GraphEdgeDefinitions));
builder.openArray();
for (auto const& it : edgeDefinitions()) {
it.second.addToBuilder(builder);
}
builder.close(); // EdgeDefinitions
// Orphan Collections
builder.add(VPackValue(StaticStrings::GraphOrphans));
builder.openArray();
for (auto const& on : _orphanColls) {
builder.add(VPackValue(on));
}
builder.close(); // Orphans
}
void Graph::enhanceEngineInfo(VPackBuilder&) const {}
// validates the type:
// edgeDefinition : { collection : string, from : [string], to : [string] }
Result EdgeDefinition::validateEdgeDefinition(
VPackSlice const& edgeDefinition) {
if (!edgeDefinition.isObject()) {
return Result(TRI_ERROR_GRAPH_CREATE_MALFORMED_EDGE_DEFINITION);
}
for (auto const& key : std::array<std::string, 3>{
{"collection", StaticStrings::GraphFrom, StaticStrings::GraphTo}}) {
if (!edgeDefinition.hasKey(key)) {
return Result(TRI_ERROR_GRAPH_INTERNAL_DATA_CORRUPT,
"Attribute '" + key + "' missing in edge definition!");
}
}
if (!edgeDefinition.get("collection").isString()) {
return Result(TRI_ERROR_GRAPH_INTERNAL_DATA_CORRUPT,
"edge definition is not a string!");
}
for (auto const& key : std::array<std::string, 2>{
{StaticStrings::GraphFrom, StaticStrings::GraphTo}}) {
if (!edgeDefinition.get(key).isArray()) {
return Result(TRI_ERROR_GRAPH_INTERNAL_DATA_CORRUPT,
"Edge definition '" + key + "' is not an array!");
}
for (auto const& it : VPackArrayIterator(edgeDefinition.get(key))) {
if (!it.isString()) {
return Result(TRI_ERROR_GRAPH_INTERNAL_DATA_CORRUPT,
std::string("Edge definition '") + key +
"' does not only contain strings!");
}
}
}
return Result();
}
// TODO: maybe create a class instance here + func as class func
// sort an edgeDefinition:
// edgeDefinition : { collection : string, from : [string], to : [string] }
std::shared_ptr<velocypack::Buffer<uint8_t>> EdgeDefinition::sortEdgeDefinition(
VPackSlice const& edgeDefinition) {
arangodb::basics::VelocyPackHelper::VPackLess<true> sorter;
VPackBuilder from = VPackCollection::sort(edgeDefinition.get(StaticStrings::GraphFrom), sorter);
VPackBuilder to = VPackCollection::sort(edgeDefinition.get(StaticStrings::GraphTo), sorter);
VPackBuilder sortedBuilder;
sortedBuilder.openObject();
sortedBuilder.add("collection", edgeDefinition.get("collection"));
sortedBuilder.add(StaticStrings::GraphFrom, from.slice());
sortedBuilder.add(StaticStrings::GraphTo, to.slice());
sortedBuilder.close();
return sortedBuilder.steal();
}
ResultT<EdgeDefinition> EdgeDefinition::createFromVelocypack(
VPackSlice edgeDefinition) {
Result res = EdgeDefinition::validateEdgeDefinition(edgeDefinition);
if (res.fail()) {
return res;
}
std::string collection = edgeDefinition.get("collection").copyString();
VPackSlice from = edgeDefinition.get(StaticStrings::GraphFrom);
VPackSlice to = edgeDefinition.get(StaticStrings::GraphTo);
std::set<std::string> fromSet;
std::set<std::string> toSet;
// duplicates in from and to shouldn't occur, but are safely ignored here
for (auto const& it : VPackArrayIterator(from)) {
fromSet.emplace(it.copyString());
}
for (auto const& it : VPackArrayIterator(to)) {
toSet.emplace(it.copyString());
}
return EdgeDefinition{collection, std::move(fromSet), std::move(toSet)};
}
bool EdgeDefinition::operator==(EdgeDefinition const& other) const {
return this->getName() == other.getName() &&
this->getFrom() == other.getFrom() && this->getTo() == other.getTo();
}
bool EdgeDefinition::operator!=(EdgeDefinition const& other) const {
return this->getName() != other.getName() ||
this->getFrom() != other.getFrom() || this->getTo() != other.getTo();
}
void EdgeDefinition::addToBuilder(VPackBuilder& builder) const {
builder.add(VPackValue(VPackValueType::Object));
builder.add("collection", VPackValue(getName()));
builder.add("from", VPackValue(VPackValueType::Array));
for (auto const& from : getFrom()) {
builder.add(VPackValue(from));
}
builder.close(); // from
// to
builder.add("to", VPackValue(VPackValueType::Array));
for (auto const& to : getTo()) {
builder.add(VPackValue(to));
}
builder.close(); // to
builder.close(); // obj
}
bool EdgeDefinition::hasFrom(std::string const &vertexCollection) const {
return getFrom().find(vertexCollection) != getFrom().end();
}
bool EdgeDefinition::hasTo(std::string const &vertexCollection) const {
return getTo().find(vertexCollection) != getTo().end();
}
bool EdgeDefinition::hasVertexCollection(const std::string &vertexCollection) const {
return hasFrom(vertexCollection) || hasTo(vertexCollection);
}
// validates the type:
// orphanDefinition : string <collectionName>
Result Graph::validateOrphanCollection(VPackSlice const& orphanCollection) {
if (!orphanCollection.isString()) {
return Result(TRI_ERROR_GRAPH_CREATE_MALFORMED_ORPHAN_LIST,
"orphan collection is not a string!");
}
return Result();
}
ResultT<EdgeDefinition const*> Graph::addEdgeDefinition(VPackSlice const& edgeDefinitionSlice) {
auto res = EdgeDefinition::createFromVelocypack(edgeDefinitionSlice);
if (res.fail()) {
return res.copy_result();
}
TRI_ASSERT(res.ok());
EdgeDefinition const& edgeDefinition = res.get();
std::string const& collection = edgeDefinition.getName();
if (hasEdgeCollection(collection)) {
return {Result(
TRI_ERROR_GRAPH_COLLECTION_MULTI_USE,
collection + " " + std::string{TRI_errno_string(
TRI_ERROR_GRAPH_COLLECTION_MULTI_USE)})};
}
_edgeColls.emplace(collection);
_edgeDefs.emplace(collection, edgeDefinition);
TRI_ASSERT(hasEdgeCollection(collection));
for (auto const& it : edgeDefinition.getFrom()) {
addVertexCollection(it);
}
for (auto const& it : edgeDefinition.getTo()) {
addVertexCollection(it);
}
return &_edgeDefs.find(collection)->second;
}
std::ostream& Graph::operator<<(std::ostream& ostream) {
ostream << "Graph \"" << name() << "\" {\n";
for (auto const& it : _edgeDefs) {
EdgeDefinition const& def = it.second;
ostream << " collection \"" << def.getName() << "\" {\n";
ostream << " from [";
bool first = true;
for (auto const& from : def.getFrom()) {
if (!first) {
ostream << ", ";
}
first = false;
ostream << from;
}
ostream << " to [";
first = true;
for (auto const& to : def.getTo()) {
if (!first) {
ostream << ", ";
}
first = false;
ostream << to;
}
ostream << " }\n";
}
ostream << "}";
return ostream;
}
bool Graph::hasEdgeCollection(std::string const& collectionName) const {
TRI_ASSERT(
(edgeDefinitions().find(collectionName) != edgeDefinitions().end()) ==
(edgeCollections().find(collectionName) != edgeCollections().end()));
return edgeCollections().find(collectionName) != edgeCollections().end();
}
bool Graph::hasVertexCollection(std::string const& collectionName) const {
return vertexCollections().find(collectionName) != vertexCollections().end();
}
bool Graph::hasOrphanCollection(std::string const& collectionName) const {
return orphanCollections().find(collectionName) != orphanCollections().end();
}
void Graph::graphForClient(VPackBuilder& builder) const {
TRI_ASSERT(builder.isOpenObject());
builder.add(VPackValue("graph"));
builder.openObject();
toPersistence(builder);
TRI_ASSERT(builder.isOpenObject());
builder.add(StaticStrings::RevString, VPackValue(rev()));
builder.add(StaticStrings::IdString, VPackValue(id()));
builder.add(StaticStrings::GraphName, VPackValue(_graphName));
builder.close(); // graph object
}
Result Graph::validateCollection(LogicalCollection& col) const {
return {TRI_ERROR_NO_ERROR};
}
void Graph::edgesToVpack(VPackBuilder& builder) const {
builder.add(VPackValue(VPackValueType::Object));
builder.add("collections", VPackValue(VPackValueType::Array));
for (auto const& edgeCollection : edgeCollections()) {
builder.add(VPackValue(edgeCollection));
}
builder.close();
builder.close();
}
void Graph::verticesToVpack(VPackBuilder& builder) const {
builder.add(VPackValue(VPackValueType::Object));
builder.add("collections", VPackValue(VPackValueType::Array));
for (auto const& vertexCollection : vertexCollections()) {
builder.add(VPackValue(vertexCollection));
}
builder.close();
builder.close();
}
bool Graph::isSmart() const {
return false;
}
void Graph::createCollectionOptions(VPackBuilder& builder, bool waitForSync) const {
TRI_ASSERT(builder.isOpenObject());
builder.add(StaticStrings::WaitForSyncString, VPackValue(waitForSync));
builder.add(StaticStrings::NumberOfShards, VPackValue(numberOfShards()));
builder.add(StaticStrings::ReplicationFactor, VPackValue(replicationFactor()));
}
boost::optional<const EdgeDefinition&> Graph::getEdgeDefinition(
std::string const& collectionName) const {
auto it = edgeDefinitions().find(collectionName);
if (it == edgeDefinitions().end()) {
TRI_ASSERT(!hasEdgeCollection(collectionName));
return boost::none;
}
TRI_ASSERT(hasEdgeCollection(collectionName));
return {it->second};
}

288
arangod/Graph/Graph.h Normal file
View File

@ -0,0 +1,288 @@
////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2018 ArangoDB 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 Tobias Gödderz
////////////////////////////////////////////////////////////////////////////////
#ifndef ARANGOD_GRAPH_GRAPH_H
#define ARANGOD_GRAPH_GRAPH_H
#include <velocypack/Buffer.h>
#include <chrono>
#include <utility>
#include "Aql/Query.h"
#include "Aql/VariableGenerator.h"
#include "Basics/ReadWriteLock.h"
#include "Cluster/ClusterInfo.h"
#include "Cluster/ResultT.h"
#include "Transaction/Methods.h"
#include "Transaction/StandaloneContext.h"
#include "Utils/OperationResult.h"
namespace arangodb {
namespace graph {
class EdgeDefinition {
public:
EdgeDefinition(std::string edgeCollection_, std::set<std::string>&& from_,
std::set<std::string>&& to_)
: _edgeCollection(std::move(edgeCollection_)), _from(from_), _to(to_) {}
std::string const& getName() const { return _edgeCollection; }
std::set<std::string> const& getFrom() const { return _from; }
std::set<std::string> const& getTo() const { return _to; }
/// @brief Adds the edge definition as a new object {collection, from, to}
/// to the builder.
void addToBuilder(velocypack::Builder& builder) const;
bool hasFrom(std::string const& vertexCollection) const;
bool hasTo(std::string const& vertexCollection) const;
bool hasVertexCollection(std::string const& vertexCollection) const;
/// @brief validate the structure of edgeDefinition, i.e.
/// that it contains the correct attributes, and that they contain the correct
/// types of values.
static Result validateEdgeDefinition(const velocypack::Slice& edgeDefinition);
static std::shared_ptr<velocypack::Buffer<uint8_t>> sortEdgeDefinition(
const velocypack::Slice& edgeDefinition);
static ResultT<EdgeDefinition> createFromVelocypack(
velocypack::Slice edgeDefinition);
bool operator==(EdgeDefinition const& other) const;
bool operator!=(EdgeDefinition const& other) const;
private:
std::string _edgeCollection;
std::set<std::string> _from;
std::set<std::string> _to;
};
class Graph {
public:
/**
* @brief Create graph from persistence.
*
* @param document The stored document
*
* @return A graph object corresponding to this document
*/
static std::unique_ptr<Graph> fromPersistence(
velocypack::Slice document, TRI_vocbase_t& vocbase);
/**
* @brief Create graph from user input.
* NOTE: This is purely in memory and will NOT persist anything.
*
* @param name The name of the Graph
* @param collectionInformation Collection information about relations and orphans
* @param options The collection creation options.
*
* @return A graph object corresponding to the user input
*/
static std::unique_ptr<Graph> fromUserInput(
std::string&& name, velocypack::Slice collectionInformation,
velocypack::Slice options);
// Wrapper for Move constructor
static std::unique_ptr<Graph> fromUserInput(
std::string const& name, velocypack::Slice collectionInformation,
velocypack::Slice options);
protected:
/**
* @brief Create graph from persistence.
*
* @param info The stored document
*/
explicit Graph(velocypack::Slice const& info);
/**
* @brief Create graph from user input.
*
* @param graphName The name of the graph
* @param info Collection information, including relations and orphans
* @param options The options to be used for collections
*/
Graph(std::string&& graphName, velocypack::Slice const& info, velocypack::Slice const& options);
public:
virtual ~Graph() = default;
static Result validateOrphanCollection(
const velocypack::Slice& orphanDefinition);
virtual void createCollectionOptions(VPackBuilder& builder,
bool waitForSync) const;
public:
/// @brief get the cids of all vertexCollections
std::unordered_set<std::string> const& vertexCollections() const;
/// @brief get the cids of all orphanCollections
std::set<std::string> const& orphanCollections() const;
/// @brief get the cids of all edgeCollections
std::set<std::string> const& edgeCollections() const;
/// @brief get the cids of all edgeCollections
std::map<std::string, EdgeDefinition> const& edgeDefinitions() const;
bool hasEdgeCollection(std::string const& collectionName) const;
bool hasVertexCollection(std::string const& collectionName) const;
bool hasOrphanCollection(std::string const& collectionName) const;
boost::optional<EdgeDefinition const&> getEdgeDefinition(
std::string const& collectionName) const;
virtual bool isSmart() const;
uint64_t numberOfShards() const;
uint64_t replicationFactor() const;
std::string const id() const;
std::string const& rev() const;
std::string const& name() const { return _graphName; }
/// @brief return a VelocyPack representation of the graph
void toVelocyPack(velocypack::Builder&) const;
/**
* @brief Create the GraphDocument to be stored in the database.
*
* @param builder The builder the result should be written in. Expects an open object.
*/
virtual void toPersistence(velocypack::Builder& builder) const;
/**
* @brief Create the Graph Json Representation to be given to the client.
* Uses toPersistence, but also includes _rev and _id values and encapsulates
* the date into a graph attribute.
*
* @param builder The builder the result should be written in. Expects an open object.
*/
void graphForClient(VPackBuilder& builder) const;
/**
* @brief Check if the collection is allowed to be used
* within this graph
*
* @param col The collection
*
* @return TRUE if we are safe to use it.
*/
virtual Result validateCollection(LogicalCollection& col) const;
void edgesToVpack(VPackBuilder& builder) const;
void verticesToVpack(VPackBuilder& builder) const;
virtual void enhanceEngineInfo(velocypack::Builder&) const;
/// @brief adds one edge definition. Returns an error if the edgeDefinition
/// is already added to this graph.
ResultT<EdgeDefinition const*> addEdgeDefinition(velocypack::Slice const& edgeDefinitionSlice);
/// @brief Add an orphan vertex collection to this graphs definition
Result addOrphanCollection(std::string&&);
std::ostream& operator<<(std::ostream& ostream);
private:
/// @brief Parse the edgeDefinition slice and inject it into this graph
void parseEdgeDefinitions(velocypack::Slice edgeDefs);
/// @brief Add a vertex collection to this graphs definition
void addVertexCollection(std::string const&);
/// @brief Add orphanCollections to the object
void insertOrphanCollections(velocypack::Slice arr);
/// @brief Set numberOfShards to the graph definition
void setNumberOfShards(uint64_t numberOfShards);
/// @brief Set replicationFactor to the graph definition
void setReplicationFactor(uint64_t setReplicationFactor);
/// @brief Set isSmart to the graph definition
void setSmartState(bool state);
/// @brief Set rev to the graph definition
void setRev(std::string&& rev);
/////////////////////////////////////////////////////////////////////////////////
//
// SECTION: Variables
//
/////////////////////////////////////////////////////////////////////////////////
protected:
/// @brief name of this graph
std::string const _graphName;
/// @brief the names of all vertexCollections
/// This includes orphans.
std::unordered_set<std::string> _vertexColls;
/// @brief the names of all orphanCollections
std::set<std::string> _orphanColls;
/// @brief the names of all edgeCollections
std::set<std::string> _edgeColls;
/// @brief edge definitions of this graph
std::map<std::string, EdgeDefinition> _edgeDefs;
/// @brief state if smart graph enabled
bool _isSmart;
/// @brief number of shards of this graph
uint64_t _numberOfShards;
/// @brief replication factor of this graph
uint64_t _replicationFactor;
/// @brief revision of this graph
std::string _rev;
};
// helper functions
template<class T, class C>
void setUnion(std::set<T> &set, C const &container) {
for(auto const& it : container) {
set.insert(it);
}
}
template<class T, class C>
void setMinus(std::set<T> &set, C const &container) {
for(auto const& it : container) {
set.erase(it);
}
}
} // namespace graph
} // namespace arangodb
#endif // ARANGOD_GRAPH_GRAPH_H

View File

@ -0,0 +1,182 @@
////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2018 ArangoDB 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 Tobias Gödderz
////////////////////////////////////////////////////////////////////////////////
#include "GraphCache.h"
#include "Graph.h"
#include <velocypack/Buffer.h>
#include <velocypack/Collection.h>
#include <velocypack/Iterator.h>
#include <velocypack/velocypack-aliases.h>
#include <array>
#include <boost/variant.hpp>
#include <utility>
#include "Aql/AstNode.h"
#include "Aql/Graphs.h"
#include "Aql/Query.h"
#include "Basics/ReadLocker.h"
#include "Basics/StaticStrings.h"
#include "Basics/VelocyPackHelper.h"
#include "Basics/WriteLocker.h"
#include "Graph/GraphManager.h"
#include "RestServer/QueryRegistryFeature.h"
#include "Transaction/Methods.h"
#include "Transaction/StandaloneContext.h"
#include "Utils/OperationOptions.h"
#include "Utils/SingleCollectionTransaction.h"
#include "VocBase/LogicalCollection.h"
#include "VocBase/Methods/Collections.h"
using namespace arangodb;
using namespace arangodb::graph;
namespace getGraphFromCacheResult {
struct Success {
std::shared_ptr<Graph const> graph;
explicit Success(std::shared_ptr<Graph const> graph_)
: graph(std::move(graph_)){};
Success& operator=(Success const& other) = default;
Success() = delete;
};
struct Outdated {};
struct NotFound {};
struct Exception {};
}
using GetGraphFromCacheResult = boost::variant<
getGraphFromCacheResult::Success, getGraphFromCacheResult::Outdated,
getGraphFromCacheResult::NotFound, getGraphFromCacheResult::Exception>;
GetGraphFromCacheResult getGraphFromCache(GraphCache::CacheType const &_cache,
std::string const &name,
std::chrono::seconds maxAge) {
using namespace getGraphFromCacheResult;
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
GraphCache::CacheType::const_iterator entryIt;
bool entryFound;
try {
entryIt = _cache.find(name);
entryFound = entryIt != _cache.end();
} catch (...) {
return Exception{};
}
if (!entryFound) {
return NotFound{};
}
GraphCache::EntryType const& entry = entryIt->second;
std::chrono::steady_clock::time_point const& insertedAt = entry.first;
if (now - insertedAt > maxAge) {
return Outdated{};
}
return Success{entry.second};
}
const std::shared_ptr<const Graph> GraphCache::getGraph(
std::shared_ptr<transaction::Context> ctx, std::string const& name,
std::chrono::seconds maxAge) {
using namespace getGraphFromCacheResult;
GetGraphFromCacheResult cacheResult = Exception{};
// try to lookup the graph in the cache first
{
READ_LOCKER(guard, _lock);
cacheResult = getGraphFromCache(_cache, name, maxAge);
}
// TODO The cache saves the graph names globally, not per database!
// This must be addressed as soon as it is activated.
/*
if (typeid(Success) == cacheResult.type()) {
LOG_TOPIC(TRACE, Logger::GRAPHS) << "GraphCache::getGraph('" << name
<< "'): Found entry in cache";
return boost::get<Success>(cacheResult).graph;
} else if (typeid(Outdated) == cacheResult.type()) {
LOG_TOPIC(TRACE, Logger::GRAPHS) << "GraphCache::getGraph('" << name
<< "'): Cached entry outdated";
} else if (typeid(NotFound) == cacheResult.type()) {
LOG_TOPIC(TRACE, Logger::GRAPHS) << "GraphCache::getGraph('" << name
<< "'): No cache entry";
} else if (typeid(Exception) == cacheResult.type()) {
LOG_TOPIC(ERR, Logger::GRAPHS)
<< "GraphCache::getGraph('" << name
<< "'): An exception occured during cache lookup";
} else {
LOG_TOPIC(FATAL, Logger::GRAPHS) << "GraphCache::getGraph('" << name
<< "'): Unhandled result type "
<< cacheResult.type().name();
return nullptr;
}*/
// if the graph wasn't found in the cache, lookup the graph and insert or
// replace the entry. if the graph doesn't exist, erase a possible entry from
// the cache.
std::unique_ptr<Graph const> graph;
try {
WRITE_LOCKER(guard, _lock);
std::chrono::steady_clock::time_point now =
std::chrono::steady_clock::now();
GraphManager gmngr{ctx->vocbase(), true};
auto result = gmngr.lookupGraphByName(name);
if (result.fail()) {
if (result.is(TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND) ||
result.is(TRI_ERROR_GRAPH_NOT_FOUND)) {
_cache.erase(name);
}
} else {
graph.reset(result.get());
}
if (graph == nullptr) {
return nullptr;
}
CacheType::iterator it;
bool insertSuccess;
std::tie(it, insertSuccess) =
_cache.emplace(name, std::make_pair(now, graph));
if (!insertSuccess) {
it->second.first = now;
it->second.second = graph;
}
} catch (...) {
};
// graph is never set to an invalid or outdated value. So even in case of an
// exception, if graph was set, it may be returned.
return graph;
}

View File

@ -0,0 +1,58 @@
////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2018 ArangoDB 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 Tobias Gödderz
////////////////////////////////////////////////////////////////////////////////
#ifndef ARANGOD_GRAPH_GRAPHCACHE_H
#define ARANGOD_GRAPH_GRAPHCACHE_H
#include <velocypack/Buffer.h>
#include <chrono>
#include <utility>
#include "Aql/Query.h"
#include "Aql/VariableGenerator.h"
#include "Basics/ReadWriteLock.h"
#include "Transaction/Methods.h"
#include "Transaction/StandaloneContext.h"
namespace arangodb {
namespace graph {
class GraphCache {
public:
// save now() along with the graph
using EntryType = std::pair<std::chrono::steady_clock::time_point,
std::shared_ptr<const Graph>>;
using CacheType = std::unordered_map<std::string, EntryType>;
// TODO The cache saves the graph names globally, not per database!
// This must be addressed as soon as it is activated.
const std::shared_ptr<const Graph> getGraph(
std::shared_ptr<transaction::Context> ctx, std::string const& name,
std::chrono::seconds maxAge = std::chrono::seconds(60));
private:
basics::ReadWriteLock _lock;
CacheType _cache;
};
} // namespace graph
} // namespace arangodb
#endif // ARANGOD_GRAPH_GRAPHCACHE_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,204 @@
////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2018 ArangoDB 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 Heiko Kernbach
////////////////////////////////////////////////////////////////////////////////
#ifndef ARANGOD_GRAPH_GRAPHMANAGER_H
#define ARANGOD_GRAPH_GRAPHMANAGER_H
#include <velocypack/Buffer.h>
#include <chrono>
#include <utility>
#include "Aql/Query.h"
#include "Aql/VariableGenerator.h"
#include "Basics/ReadWriteLock.h"
#include "Cluster/ClusterInfo.h"
#include "Cluster/ResultT.h"
#include "Graph/Graph.h"
#include "Transaction/Methods.h"
#include "Transaction/StandaloneContext.h"
#include "Utils/OperationResult.h"
namespace arangodb {
namespace graph {
class GraphManager {
private:
TRI_vocbase_t& _vocbase;
bool _isInTransaction;
std::shared_ptr<transaction::Context> ctx() const;
////////////////////////////////////////////////////////////////////////////////
/// @brief find or create vertex collection by name
////////////////////////////////////////////////////////////////////////////////
OperationResult findOrCreateVertexCollectionByName(const std::string& name,
bool waitForSync, VPackSlice options);
////////////////////////////////////////////////////////////////////////////////
/// @brief find or create collection by name and type
////////////////////////////////////////////////////////////////////////////////
OperationResult createCollection(std::string const& name, TRI_col_type_e colType,
bool waitForSync, VPackSlice options);
public:
explicit GraphManager(TRI_vocbase_t& vocbase) : GraphManager(vocbase, false) {};
GraphManager(TRI_vocbase_t& vocbase, bool isInTransaction)
: _vocbase(vocbase), _isInTransaction(isInTransaction) {
}
OperationResult readGraphs(velocypack::Builder& builder,
arangodb::aql::QueryPart queryPart) const;
OperationResult readGraphKeys(velocypack::Builder& builder,
arangodb::aql::QueryPart queryPart) const;
OperationResult readGraphByQuery(velocypack::Builder& builder,
arangodb::aql::QueryPart queryPart,
std::string queryStr) const;
////////////////////////////////////////////////////////////////////////////////
/// @brief find and return a collections if available
////////////////////////////////////////////////////////////////////////////////
static std::shared_ptr<LogicalCollection> getCollectionByName(
const TRI_vocbase_t& vocbase, std::string const& name);
////////////////////////////////////////////////////////////////////////////////
/// @brief checks wheter a graph exists or not
////////////////////////////////////////////////////////////////////////////////
bool graphExists(std::string const& graphName) const;
////////////////////////////////////////////////////////////////////////////////
/// @brief lookup a graph by name
////////////////////////////////////////////////////////////////////////////////
ResultT<std::unique_ptr<Graph>> lookupGraphByName(std::string const& name) const;
////////////////////////////////////////////////////////////////////////////////
/// @brief create a graph
////////////////////////////////////////////////////////////////////////////////
OperationResult createGraph(VPackSlice document, bool waitForSync) const;
////////////////////////////////////////////////////////////////////////////////
/// @brief find or create collections by EdgeDefinitions
////////////////////////////////////////////////////////////////////////////////
OperationResult findOrCreateCollectionsByEdgeDefinitions(
std::map<std::string, EdgeDefinition> const& edgeDefinitions,
bool waitForSync, VPackSlice options);
OperationResult findOrCreateCollectionsByEdgeDefinition(
EdgeDefinition const& edgeDefinition, bool waitForSync,
VPackSlice options);
////////////////////////////////////////////////////////////////////////////////
/// @brief create a vertex collection
////////////////////////////////////////////////////////////////////////////////
OperationResult createVertexCollection(std::string const& name,
bool waitForSync, VPackSlice options);
////////////////////////////////////////////////////////////////////////////////
/// @brief create an edge collection
////////////////////////////////////////////////////////////////////////////////
OperationResult createEdgeCollection(std::string const& name,
bool waitForSync, VPackSlice options);
/// @brief rename a collection used in an edge definition
bool renameGraphCollection(std::string oldName, std::string newName);
/// @brief check if the edge definitions conflicts with one in an existing
/// graph
Result checkForEdgeDefinitionConflicts(
std::map<std::string, arangodb::graph::EdgeDefinition> const&
edgeDefinitions) const;
/// @brief check if the edge definition conflicts with one in an existing
/// graph
Result checkForEdgeDefinitionConflicts(
arangodb::graph::EdgeDefinition const& edgeDefinition) const;
/// @brief Remove a graph and optional all connected collections
OperationResult removeGraph(Graph const& graph, bool waitForSync,
bool dropCollections);
OperationResult pushCollectionIfMayBeDropped(
const std::string& colName, const std::string& graphName,
std::unordered_set<std::string>& toBeRemoved);
bool collectionExists(std::string const& collection) const;
/**
* @brief Helper function to make sure all collections required
* for this graph are created or exist.
* Will fail if the collections cannot be created, or
* if they have a non-compatible sharding in SmartGraph-case.
*
* @param graph The graph information used to make sure all collections exist
*
* @return Either OK or an error.
*/
Result ensureCollections(Graph const* graph, bool waitForSync) const;
/**
* @brief Store the given graph
*
* @param graph The graph to store
* @param waitForSync Wait for Collection to sync
* @param isUpdate If it is an update on existing graph or a new one.
*
* @return The result of the insrt transaction or Error.
*/
OperationResult storeGraph(Graph const& graph, bool waitForSync, bool isUpdate) const;
private:
#ifdef USE_ENTERPRISE
Result ensureSmartCollectionSharding(Graph const* graph, bool waitForSync, std::unordered_set<std::string>& documentCollections) const;
#endif
/**
* @brief Create a new in memory graph object from the given input.
* This graph object does not create collections and does
* not check them. It cannot be used to access any kind
* of data. In order to get a "usable" Graph object use
* lookup by name.
*
* @param input The slice containing the graph data.
*
* @return A temporary Graph object
*/
ResultT<std::unique_ptr<Graph>> buildGraphFromInput(std::string const& graphName, arangodb::velocypack::Slice input) const;
Result checkCreateGraphPermissions(Graph const* graph) const;
Result checkDropGraphPermissions(
Graph const& graph,
std::unordered_set<std::string> const& followersToBeRemoved,
std::unordered_set<std::string> const& leadersToBeRemoved);
};
} // namespace graph
} // namespace arangodb
#endif // ARANGOD_GRAPH_GRAPHMANAGER_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,211 @@
////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2018 ArangoDB 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 Tobias Gödderz & Heiko Kernbach
////////////////////////////////////////////////////////////////////////////////
#ifndef ARANGOD_GRAPH_GRAPHOPERATIONS_H
#define ARANGOD_GRAPH_GRAPHOPERATIONS_H
#include <velocypack/Buffer.h>
#include <velocypack/velocypack-aliases.h>
#include <chrono>
#include <utility>
#include "Aql/Query.h"
#include "Aql/VariableGenerator.h"
#include "Auth/Common.h"
#include "Basics/ReadWriteLock.h"
#include "Cluster/ClusterInfo.h"
#include "Cluster/ResultT.h"
#include "Graph/Graph.h"
#include "Transaction/Methods.h"
#include "Transaction/StandaloneContext.h"
#include "Utils/OperationResult.h"
namespace arangodb {
namespace graph {
// TODO rename to GraphMethods
class GraphOperations {
private:
Graph& _graph;
TRI_vocbase_t& _vocbase;
Graph const& graph() const { return _graph; };
std::shared_ptr<transaction::Context> ctx() const;
public:
GraphOperations() = delete;
GraphOperations(Graph& graph_,
TRI_vocbase_t& vocbase)
: _graph(graph_), _vocbase(vocbase) {}
// TODO I added the complex result type for the get* methods to exactly
// reproduce (in the RestGraphHandler) the behaviour of the similar methods
// in the RestDocumentHandler. A simpler type, e.g. ResultT<OperationResult>,
// would be preferable.
/// @brief Get a single vertex document from collection, optionally check rev
/// The return value is as follows:
/// If trx.begin fails, the outer ResultT will contain this error Result.
/// Otherwise, the results of both (trx.document(), trx.finish()) are
/// returned as a pair.
/// This is because in case of a precondition error during trx.document(),
/// the OperationResult may still be needed.
OperationResult getVertex(std::string const& collectionName,
std::string const& key,
boost::optional<TRI_voc_rid_t> rev);
/// @brief Get a single edge document from definitionName.
/// Similar to getVertex().
OperationResult getEdge(const std::string& definitionName,
const std::string& key,
boost::optional<TRI_voc_rid_t> rev);
/// @brief Remove a single edge document from definitionName.
OperationResult removeEdge(const std::string& definitionName,
const std::string& key,
boost::optional<TRI_voc_rid_t> rev,
bool waitForSync, bool returnOld);
/// @brief Remove a vertex and all incident edges in the graph
OperationResult removeVertex(const std::string& collectionName,
const std::string& key,
boost::optional<TRI_voc_rid_t> rev,
bool waitForSync, bool returnOld);
OperationResult updateEdge(const std::string& definitionName,
const std::string& key, VPackSlice document,
boost::optional<TRI_voc_rid_t> rev,
bool waitForSync, bool returnOld, bool returnNew,
bool keepNull);
OperationResult replaceEdge(const std::string& definitionName,
const std::string& key, VPackSlice document,
boost::optional<TRI_voc_rid_t> rev,
bool waitForSync, bool returnOld, bool returnNew,
bool keepNull);
OperationResult createEdge(const std::string& definitionName,
VPackSlice document, bool waitForSync,
bool returnNew);
OperationResult updateVertex(const std::string& collectionName,
const std::string& key, VPackSlice document,
boost::optional<TRI_voc_rid_t> rev,
bool waitForSync, bool returnOld, bool returnNew,
bool keepNull);
OperationResult replaceVertex(const std::string& collectionName,
const std::string& key, VPackSlice document,
boost::optional<TRI_voc_rid_t> rev,
bool waitForSync, bool returnOld,
bool returnNew, bool keepNull);
OperationResult createVertex(const std::string& collectionName,
VPackSlice document, bool waitForSync,
bool returnNew);
////////////////////////////////////////////////////////////////////////////////
/// @brief add an orphan to collection to an existing graph
////////////////////////////////////////////////////////////////////////////////
OperationResult addOrphanCollection(VPackSlice document, bool waitForSync,
bool createCollection);
////////////////////////////////////////////////////////////////////////////////
/// @brief remove an orphan collection from an existing graph
////////////////////////////////////////////////////////////////////////////////
OperationResult eraseOrphanCollection(bool waitForSync,
std::string collectionName,
bool dropCollection);
////////////////////////////////////////////////////////////////////////////////
/// @brief create a new edge definition in an existing graph
////////////////////////////////////////////////////////////////////////////////
OperationResult addEdgeDefinition(VPackSlice edgeDefinition,
bool waitForSync);
////////////////////////////////////////////////////////////////////////////////
/// @brief remove an edge definition from an existing graph
////////////////////////////////////////////////////////////////////////////////
OperationResult eraseEdgeDefinition(bool waitForSync,
std::string edgeDefinitionName,
bool dropCollection);
////////////////////////////////////////////////////////////////////////////////
/// @brief create edge definition in an existing graph
////////////////////////////////////////////////////////////////////////////////
OperationResult editEdgeDefinition(VPackSlice edgeDefinitionSlice,
bool waitForSync,
const std::string& edgeDefinitionName);
////////////////////////////////////////////////////////////////////////////////
/// @brief change the edge definition for a specified graph
/// if the graph doesn't already contain a definition for the same edge
/// collection, this does nothing and returns success.
////////////////////////////////////////////////////////////////////////////////
OperationResult changeEdgeDefinitionForGraph(
const Graph& graph, const EdgeDefinition& edgeDefinition,
bool waitForSync, transaction::Methods& trx);
private:
using VPackBufferPtr = std::shared_ptr<velocypack::Buffer<uint8_t>>;
OperationResult getDocument(std::string const& collectionName,
const std::string& key,
boost::optional<TRI_voc_rid_t> rev);
/// @brief creates a vpack { _key: key } or { _key: key, _rev: rev }
/// (depending on whether rev is set)
VPackBufferPtr _getSearchSlice(const std::string& key,
boost::optional<TRI_voc_rid_t>& rev) const;
OperationResult modifyDocument(const std::string& collectionName,
const std::string& key, VPackSlice document,
bool isPatch,
boost::optional<TRI_voc_rid_t> rev,
bool waitForSync, bool returnOld,
bool returnNew, bool keepNull);
OperationResult createDocument(transaction::Methods* trx,
const std::string& collectionName,
VPackSlice document, bool waitForSync,
bool returnNew);
OperationResult checkEdgeCollectionAvailability(
std::string edgeCollectionName);
OperationResult checkVertexCollectionAvailability(
std::string vertexCollectionName);
bool hasROPermissionsFor(std::string const& collection) const;
bool hasRWPermissionsFor(std::string const& collection) const;
bool hasPermissionsFor(std::string const& collection,
auth::Level level) const;
Result checkEdgeDefinitionPermissions(
EdgeDefinition const& edgeDefinition) const;
bool collectionExists(std::string const& collection) const;
};
} // namespace graph
} // namespace arangodb
#endif // ARANGOD_GRAPH_GRAPHOPERATIONS_H

View File

@ -24,7 +24,6 @@
#include "RestControlPregelHandler.h"
#include "ApplicationFeatures/ApplicationServer.h"
#include "Aql/Graphs.h"
#include "Basics/VelocyPackHelper.h"
#include "Cluster/ServerState.h"
#include "Pregel/Conductor.h"
@ -33,7 +32,8 @@
#include "Transaction/StandaloneContext.h"
#include "V8/v8-vpack.h"
#include "V8Server/V8DealerFeature.h"
#include "VocBase/Graphs.h"
#include "Graph/Graph.h"
#include "Graph/GraphManager.h"
#include "VocBase/Methods/Tasks.h"
#include <velocypack/Builder.h>
@ -133,12 +133,13 @@ void RestControlPregelHandler::startExecution() {
return;
}
auto ctx = transaction::StandaloneContext::Create(_vocbase);
auto graph = lookupGraphByName(ctx, gs);
if (nullptr == graph) {
generateError(TRI_ERROR_GRAPH_NOT_FOUND);
graph::GraphManager gmngr{_vocbase};
auto graphRes = gmngr.lookupGraphByName(gs);
if (graphRes.fail()) {
generateError(graphRes.copy_result());
return;
}
std::unique_ptr<graph::Graph> graph = std::move(graphRes.get());
auto gv = graph->vertexCollections();
for (auto& v : gv) {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,278 @@
////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2018 ArangoDB 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 Tobias Gödderz
////////////////////////////////////////////////////////////////////////////////
#ifndef ARANGOD_REST_HANDLER_REST_GRAPH_HANDLER_H
#define ARANGOD_REST_HANDLER_REST_GRAPH_HANDLER_H
#include <boost/optional.hpp>
#include "Actions/RestActionHandler.h"
#include "Graph/GraphManager.h"
#include "RestHandler/RestBaseHandler.h"
namespace arangodb {
namespace graph {
class Graph;
}
class RestGraphHandler : public arangodb::RestVocbaseBaseHandler {
private:
enum class GraphProperty {
VERTICES, EDGES
};
enum class EdgeDefinitionAction {
CREATE, EDIT, REMOVE
};
enum class VertexDefinitionAction {
CREATE, REMOVE
};
public:
RestGraphHandler(GeneralRequest* request, GeneralResponse* response);
~RestGraphHandler() override = default;
char const* name() const final { return "RestGraphHandler"; }
RestStatus execute() override;
RequestLane lane() const override;
private:
arangodb::Result executeGharial();
// /_api/gharial
arangodb::Result graphsAction();
// /_api/gharial/{graph-name}
arangodb::Result graphAction(
graph::Graph& graph);
// /_api/gharial/{graph-name}/vertex
arangodb::Result vertexSetsAction(
graph::Graph& graph);
// /_api/gharial/{graph-name}/edge
arangodb::Result edgeSetsAction(
graph::Graph& graph);
// /_api/gharial/{graph-name}/vertex/{collection-name}
arangodb::Result vertexSetAction(
graph::Graph& graph,
const std::string& vertexCollectionName);
// /_api/gharial/{graph-name}/edge/{definition-name}
arangodb::Result edgeSetAction(
graph::Graph& graph,
const std::string& edgeDefinitionName);
// /_api/gharial/{graph-name}/vertex/{collection-name}/{vertex-key}
arangodb::Result vertexAction(
graph::Graph& graph,
const std::string& vertexCollectionName, const std::string& vertexKey);
// /_api/gharial/{graph-name}/edge/{definition-name}/{edge-key}
arangodb::Result edgeAction(
graph::Graph& graph,
const std::string& edgeDefinitionName, const std::string& edgeKey);
// GET /_api/gharial/{graph-name}/vertex/{collection-name}/{vertex-key}
void vertexActionRead(graph::Graph& graph,
const std::string &collectionName,
const std::string &key);
// DELETE /_api/gharial/{graph-name}/vertex/{collection-name}/{vertex-key}
arangodb::Result vertexActionRemove(graph::Graph& graph,
const std::string& collectionName,
const std::string& key);
// PATCH /_api/gharial/{graph-name}/vertex/{collection-name}/{vertex-key}
arangodb::Result vertexActionUpdate(graph::Graph& graph,
const std::string& collectionName,
const std::string& key);
// PUT /_api/gharial/{graph-name}/vertex/{collection-name}/{vertex-key}
arangodb::Result vertexActionReplace(graph::Graph& graph,
const std::string& collectionName,
const std::string& key);
// POST /_api/gharial/{graph-name}/vertex/{collection-name}/{vertex-key}
arangodb::Result vertexActionCreate(graph::Graph& graph,
const std::string& collectionName);
// GET /_api/gharial/{graph-name}/edge/{definition-name}/{edge-key}
void edgeActionRead(graph::Graph& graph,
const std::string &definitionName,
const std::string &key);
// DELETE /_api/gharial/{graph-name}/edge/{definition-name}/{edge-key}
arangodb::Result edgeActionRemove(graph::Graph& graph,
const std::string& definitionName,
const std::string& key);
// POST /_api/gharial/{graph-name}/edge/{definition-name}/{edge-key}
arangodb::Result edgeActionCreate(graph::Graph& graph,
const std::string& definitionName);
// PATCH /_api/gharial/{graph-name}/edge/{definition-name}/{edge-key}
arangodb::Result edgeActionUpdate(graph::Graph& graph,
const std::string& collectionName,
const std::string& key);
// PUT /_api/gharial/{graph-name}/edge/{definition-name}/{edge-key}
arangodb::Result edgeActionReplace(graph::Graph& graph,
const std::string& collectionName,
const std::string& key);
std::unique_ptr<graph::Graph> getGraph(const std::string& graphName);
void generateVertexRead(VPackSlice vertex, VPackOptions const& options);
void generateEdgeRead(VPackSlice edge, const VPackOptions& options);
void generateRemoved(bool removed, bool wasSynchronous, VPackSlice old,
VPackOptions const& options);
void generateGraphRemoved(bool removed, bool wasSynchronous,
VPackOptions const& options);
void generateCreatedGraphConfig(bool wasSynchronous, VPackSlice slice,
VPackOptions const& options);
void generateCreatedEdgeDefinition(bool wasSynchronous, VPackSlice slice,
VPackOptions const& options);
void generateGraphConfig(VPackSlice slice, VPackOptions const& options);
// TODO maybe cleanup the generate* zoo a little?
void generateResultWithField(std::string const& key, VPackSlice value,
VPackOptions const& options);
void generateResultMergedWithObject(VPackSlice obj,
VPackOptions const& options);
void addEtagHeader(velocypack::Slice slice);
Result vertexModify(graph::Graph& graph,
const std::string& collectionName, const std::string& key,
bool isPatch);
Result vertexCreate(graph::Graph& graph,
const std::string& collectionName);
void generateVertexModified(bool wasSynchronous, VPackSlice resultSlice,
const velocypack::Options& options);
void generateVertexCreated(bool wasSynchronous, VPackSlice resultSlice,
const velocypack::Options& options);
void generateModified(TRI_col_type_e colType, bool wasSynchronous,
VPackSlice resultSlice,
const velocypack::Options& options);
void generateCreated(TRI_col_type_e colType, bool wasSynchronous,
VPackSlice resultSlice,
const velocypack::Options& options);
Result edgeModify(graph::Graph& graph,
const std::string& collectionName, const std::string& key,
bool isPatch);
Result edgeCreate(graph::Graph& graph,
const std::string& collectionName);
Result documentModify(graph::Graph& graph,
const std::string& collectionName,
const std::string& key, bool isPatch,
TRI_col_type_e colType);
Result documentCreate(
graph::Graph& graph, const std::string &collectionName,
TRI_col_type_e colType
);
Result graphActionReadGraphConfig(
graph::Graph const& graph
);
Result graphActionRemoveGraph(
graph::Graph const& graph
);
Result graphActionCreateGraph();
Result graphActionReadGraphs();
Result graphActionReadConfig(
graph::Graph const& graph,
TRI_col_type_e colType, GraphProperty property
);
void generateEdgeModified(
bool wasSynchronous, VPackSlice resultSlice,
velocypack::Options const& options
);
void generateEdgeCreated(
bool wasSynchronous, VPackSlice resultSlice,
velocypack::Options const& options
);
// edges
// PATCH /_api/gharial/{graph-name}/edge/{definition-name}
Result editEdgeDefinition(
graph::Graph& graph,
const std::string& edgeDefinitionName
);
// DELETE /_api/gharial/{graph-name}/edge/{definition-name}
Result removeEdgeDefinition(
graph::Graph& graph,
const std::string& edgeDefinitionName
);
// POST /_api/gharial/{graph-name}/edge/
Result createEdgeDefinition(
graph::Graph& graph
);
// edgeDefinitionName may be omitted when action == CREATE
Result modifyEdgeDefinition(
graph::Graph& graph,
EdgeDefinitionAction action,
std::string edgeDefinitionName = {}
);
Result modifyVertexDefinition(
graph::Graph& graph,
VertexDefinitionAction action,
std::string vertexDefinitionName
);
private:
graph::GraphManager _gmngr;
};
} // namespace arangodb
#endif // ARANGOD_REST_HANDLER_REST_GRAPH_HANDLER_H

View File

@ -193,6 +193,7 @@ RestStatus RestRepairHandler::repairDistributeShardsLike() {
if (ClusterInfo* clusterInfo = ClusterInfo::instance()) {
clusterInfo->loadPlan();
}
}
return RestStatus::DONE;
}

View File

@ -102,6 +102,12 @@ std::string const RestVocbaseBaseHandler::DOCUMENT_PATH = "/_api/document";
std::string const RestVocbaseBaseHandler::EDGES_PATH = "/_api/edges";
////////////////////////////////////////////////////////////////////////////////
/// @brief gharial graph api path
////////////////////////////////////////////////////////////////////////////////
std::string const RestVocbaseBaseHandler::GHARIAL_PATH = "/_api/gharial";
////////////////////////////////////////////////////////////////////////////////
/// @brief endpoint path
////////////////////////////////////////////////////////////////////////////////

View File

@ -103,6 +103,12 @@ class RestVocbaseBaseHandler : public RestBaseHandler {
static std::string const EDGES_PATH;
//////////////////////////////////////////////////////////////////////////////
/// @brief gharial graph api path
//////////////////////////////////////////////////////////////////////////////
static std::string const GHARIAL_PATH;
//////////////////////////////////////////////////////////////////////////////
/// @brief endpoint path
//////////////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,770 @@
////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 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 Heiko Kernbach
/// @author Copyright 2018, ArangoDB GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
#include "v8-users.h"
#include "ApplicationFeatures/ApplicationServer.h"
#include "Basics/VelocyPackHelper.h"
#include "GeneralServer/AuthenticationFeature.h"
#include "Graph/Graph.h"
#include "Graph/GraphManager.h"
#include "Graph/GraphOperations.h"
#include "RestServer/DatabaseFeature.h"
#include "Transaction/V8Context.h"
#include "Utils/ExecContext.h"
#include "V8/v8-conv.h"
#include "V8/v8-globals.h"
#include "V8/v8-utils.h"
#include "V8/v8-vpack.h"
#include "V8Server/v8-externals.h"
#include "V8Server/v8-vocbase.h"
#include "V8Server/v8-vocbaseprivate.h"
#include "V8Server/v8-vocindex.h"
#include "VocBase/LogicalCollection.h"
#include <velocypack/HexDump.h>
#include <velocypack/Slice.h>
using namespace arangodb;
using namespace arangodb::velocypack;
using namespace arangodb::basics;
using namespace arangodb::graph;
using namespace arangodb::rest;
static void JS_DropGraph(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
if (args.Length() < 1) {
TRI_V8_THROW_EXCEPTION_USAGE("_drop(graphName, dropCollections)");
} else if (!args[0]->IsString()) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_GRAPH_CREATE_MISSING_NAME);
}
std::string graphName = TRI_ObjectToString(args[0]);
if (graphName.empty()) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_GRAPH_CREATE_MISSING_NAME);
}
bool dropCollections = false;
if (args.Length() >= 2) {
dropCollections = TRI_ObjectToBoolean(args[1]);
}
auto& vocbase = GetContextVocBase(isolate);
auto ctx = transaction::V8Context::Create(vocbase, false);
GraphManager gmngr{vocbase};
auto graph = gmngr.lookupGraphByName(graphName);
if (graph.fail()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(graph.errorNumber(), graph.errorMessage());
}
TRI_ASSERT(graph.get() != nullptr);
OperationResult result = gmngr.removeGraph(*(graph.get()), true, dropCollections);
VPackBuilder obj;
obj.add(VPackValue(VPackValueType::Object, true));
obj.add("removed", VPackValue(result.ok()));
obj.close();
TRI_V8_RETURN(TRI_VPackToV8(isolate, obj.slice()));
TRI_V8_TRY_CATCH_END
}
static void JS_RenameGraphCollection(
v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
if (args.Length() < 2) {
TRI_V8_THROW_EXCEPTION_USAGE("_renameCollection(oldName, newName)");
} else if (!args[0]->IsString()) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_GRAPH_CREATE_MISSING_NAME);
} else if (!args[1]->IsString()) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_GRAPH_CREATE_MISSING_NAME);
}
std::string oldName = TRI_ObjectToString(args[0]);
std::string newName = TRI_ObjectToString(args[1]);
if (oldName.empty() || newName.empty()) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_GRAPH_CREATE_MISSING_NAME);
}
auto& vocbase = GetContextVocBase(isolate);
GraphManager gmngr{vocbase};
bool r = gmngr.renameGraphCollection(oldName, newName);
TRI_V8_RETURN(r);
TRI_V8_RETURN_UNDEFINED();
TRI_V8_TRY_CATCH_END
}
static void JS_GraphExists(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
if (args.Length() < 1) {
TRI_V8_THROW_EXCEPTION_USAGE("_exists(graphName)");
} else if (!args[0]->IsString()) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_GRAPH_CREATE_MISSING_NAME);
}
std::string graphName = TRI_ObjectToString(args[0]);
if (graphName.empty()) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_GRAPH_CREATE_MISSING_NAME);
}
auto& vocbase = GetContextVocBase(isolate);
// check if graph already exists
GraphManager gmngr{vocbase};
bool r = gmngr.graphExists(graphName);
TRI_V8_RETURN(r);
TRI_V8_TRY_CATCH_END
}
static void JS_GetGraph(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
if (args.Length() < 1) {
TRI_V8_THROW_EXCEPTION_USAGE("_graph(graphName)");
} else if (!args[0]->IsString()) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_GRAPH_CREATE_MISSING_NAME);
}
std::string graphName = TRI_ObjectToString(args[0]);
if (graphName.empty()) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_GRAPH_CREATE_MISSING_NAME);
}
auto& vocbase = GetContextVocBase(isolate);
GraphManager gmngr{vocbase};
auto graph = gmngr.lookupGraphByName(graphName);
if (graph.fail()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(graph.errorNumber(), graph.errorMessage());
}
TRI_ASSERT(graph.get() != nullptr);
VPackBuilder result;
result.openObject();
graph.get()->graphForClient(result);
result.close();
VPackSlice resSlice = result.slice().get("graph");
TRI_V8_RETURN(TRI_VPackToV8(isolate, resSlice));
TRI_V8_TRY_CATCH_END
}
static void JS_GetGraphs(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
auto& vocbase = GetContextVocBase(isolate);
GraphManager gmngr{vocbase};
VPackBuilder result;
OperationResult r = gmngr.readGraphs(result, arangodb::aql::PART_DEPENDENT);
if (r.fail()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(r.errorNumber(), r.errorMessage());
}
if (!result.isEmpty()) {
TRI_V8_RETURN(TRI_VPackToV8(isolate, result.slice().get("graphs")));
}
TRI_V8_RETURN_UNDEFINED();
TRI_V8_TRY_CATCH_END
}
static void JS_GetGraphKeys(v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
auto& vocbase = GetContextVocBase(isolate);
GraphManager gmngr{vocbase};
VPackBuilder result;
OperationResult r =
gmngr.readGraphKeys(result, arangodb::aql::PART_DEPENDENT);
if (r.fail()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(r.errorNumber(), r.errorMessage());
}
if (!result.isEmpty()) {
TRI_V8_RETURN(TRI_VPackToV8(isolate, result.slice().get("graphs")));
}
TRI_V8_RETURN_UNDEFINED();
TRI_V8_TRY_CATCH_END
}
static void JS_CreateGraph(v8::FunctionCallbackInfo<v8::Value> const& args) {
// TODO Needs to return a wrapped Graph!
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
if (args.Length() < 1) {
TRI_V8_THROW_EXCEPTION_USAGE(
"_create(graphName, edgeDefinitions, orphanCollections, options)");
} else if (!args[0]->IsString()) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_GRAPH_CREATE_MISSING_NAME);
}
std::string graphName = TRI_ObjectToString(args[0]);
if (graphName.empty()) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_GRAPH_CREATE_MISSING_NAME);
}
VPackBuilder builder;
builder.openObject();
builder.add("name", VPackValue(graphName));
if (args.Length() >= 2 && !args[1]->IsNullOrUndefined()) {
builder.add(VPackValue(StaticStrings::GraphEdgeDefinitions));
TRI_V8ToVPack(isolate, builder, args[1], true, true);
builder.close();
}
if (args.Length() >= 3 && !args[2]->IsNullOrUndefined()) {
builder.add(VPackValue(StaticStrings::GraphOrphans));
TRI_V8ToVPack(isolate, builder, args[2], true, true);
builder.close();
}
if (args.Length() >= 4 && !args[3]->IsNullOrUndefined()) {
builder.add(VPackValue("options"));
TRI_V8ToVPack(isolate, builder, args[3], true, true);
builder.close();
}
builder.close();
auto& vocbase = GetContextVocBase(isolate);
GraphManager gmngr{vocbase};
OperationResult r = gmngr.createGraph(builder.slice(), false);
if (r.fail()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(r.errorNumber(), r.errorMessage());
}
auto graph = gmngr.lookupGraphByName(graphName);
if (graph.fail()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(graph.errorNumber(), graph.errorMessage());
}
TRI_ASSERT(graph.get() != nullptr);
VPackBuilder result;
result.openObject();
graph.get()->graphForClient(result);
result.close();
TRI_V8_RETURN(TRI_VPackToV8(isolate, result.slice()));
TRI_V8_TRY_CATCH_END
}
static void JS_AddEdgeDefinitions(
v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
if (args.Length() < 2) {
TRI_V8_THROW_EXCEPTION_USAGE("_extendEdgeDefinitions(edgeDefinition)");
}
if (!args[0]->IsString()) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_GRAPH_CREATE_MISSING_NAME);
}
std::string graphName = TRI_ObjectToString(args[0]);
if (graphName.empty()) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_GRAPH_CREATE_MISSING_NAME);
}
VPackBuilder edgeDefinition;
TRI_V8ToVPack(isolate, edgeDefinition, args[1], false);
auto& vocbase = GetContextVocBase(isolate);
GraphManager gmngr{vocbase};
auto graph = gmngr.lookupGraphByName(graphName);
if (graph.fail()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(graph.errorNumber(), graph.errorMessage());
}
TRI_ASSERT(graph.get() != nullptr);
GraphOperations gops{*graph.get(), vocbase};
OperationResult r = gops.addEdgeDefinition(edgeDefinition.slice(), false);
if (r.fail()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(r.errorNumber(), r.errorMessage());
}
graph = gmngr.lookupGraphByName(graphName);
if (graph.fail()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(graph.errorNumber(), graph.errorMessage());
}
TRI_ASSERT(graph.get() != nullptr);
VPackBuilder result;
result.openObject();
graph.get()->graphForClient(result);
result.close();
TRI_V8_RETURN(TRI_VPackToV8(isolate, result.slice()));
TRI_V8_TRY_CATCH_END
}
static void JS_EditEdgeDefinitions(
v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
if (args.Length() < 2) {
TRI_V8_THROW_EXCEPTION_USAGE("_editEdgeDefinitions(edgeDefinition)");
}
if (!args[0]->IsString()) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_GRAPH_CREATE_MISSING_NAME);
}
std::string graphName = TRI_ObjectToString(args[0]);
if (graphName.empty()) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_GRAPH_CREATE_MISSING_NAME);
}
VPackBuilder edgeDefinition;
TRI_V8ToVPack(isolate, edgeDefinition, args[1], false);
auto& vocbase = GetContextVocBase(isolate);
GraphManager gmngr{vocbase};
auto graph = gmngr.lookupGraphByName(graphName);
if (graph.fail()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(graph.errorNumber(), graph.errorMessage());
}
TRI_ASSERT(graph.get() != nullptr);
GraphOperations gops{*(graph.get()), vocbase};
OperationResult r = gops.editEdgeDefinition(
edgeDefinition.slice(), false,
edgeDefinition.slice().get("collection").copyString());
if (r.fail()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(r.errorNumber(), r.errorMessage());
}
graph = gmngr.lookupGraphByName(graphName);
if (graph.fail()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(graph.errorNumber(), graph.errorMessage());
}
TRI_ASSERT(graph.get() != nullptr);
VPackBuilder result;
result.openObject();
graph.get()->graphForClient(result);
result.close();
TRI_V8_RETURN(TRI_VPackToV8(isolate, result.slice()));
TRI_V8_TRY_CATCH_END
}
static void JS_RemoveVertexCollection(
v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
if (args.Length() < 2) {
TRI_V8_THROW_EXCEPTION_USAGE(
"_removeVertexCollection(vertexName, dropCollection)");
}
if (!args[0]->IsString()) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_GRAPH_CREATE_MISSING_NAME);
}
if (!args[1]->IsString()) {
TRI_V8_THROW_EXCEPTION_USAGE(
"_removeVertexCollection(vertexName, dropCollection)");
}
std::string graphName = TRI_ObjectToString(args[0]);
std::string vertexName = TRI_ObjectToString(args[1]);
if (graphName.empty()) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_GRAPH_CREATE_MISSING_NAME);
}
if (vertexName.empty()) {
TRI_V8_THROW_EXCEPTION_USAGE(
"_removeVertexCollection(vertexName, dropCollection)");
}
bool dropCollection = false;
if (args.Length() >= 3) {
dropCollection = TRI_ObjectToBoolean(args[2]);
}
auto& vocbase = GetContextVocBase(isolate);
GraphManager gmngr{vocbase};
auto graph = gmngr.lookupGraphByName(graphName);
if (graph.fail()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(graph.errorNumber(), graph.errorMessage());
}
TRI_ASSERT(graph.get() != nullptr);
VPackBuilder builder;
builder.openObject();
builder.add("collection", VPackValue(vertexName));
builder.close();
GraphOperations gops{*(graph.get()), vocbase};
OperationResult r = gops.eraseOrphanCollection(false, vertexName, dropCollection);
if (r.fail()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(r.errorNumber(), r.errorMessage());
}
graph = gmngr.lookupGraphByName(graphName);
if (graph.fail()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(graph.errorNumber(), graph.errorMessage());
}
TRI_ASSERT(graph.get() != nullptr);
VPackBuilder result;
result.openObject();
graph.get()->graphForClient(result);
result.close();
TRI_V8_RETURN(TRI_VPackToV8(isolate, result.slice()));
TRI_V8_TRY_CATCH_END
}
static void JS_AddVertexCollection(
v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
if (args.Length() < 2) {
TRI_V8_THROW_EXCEPTION_USAGE(
"_addVertexCollection(vertexName, createCollection)");
}
if (!args[0]->IsString()) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_GRAPH_CREATE_MISSING_NAME);
}
if (!args[1]->IsString()) {
TRI_V8_THROW_EXCEPTION_USAGE(
"_addVertexCollection(vertexName, createCollection)");
}
std::string graphName = TRI_ObjectToString(args[0]);
std::string vertexName = TRI_ObjectToString(args[1]);
if (graphName.empty()) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_GRAPH_CREATE_MISSING_NAME);
}
if (vertexName.empty()) {
TRI_V8_THROW_EXCEPTION_USAGE(
"_addVertexCollection(vertexName, createCollection)");
}
bool createCollection = true;
if (args.Length() >= 3) {
createCollection = TRI_ObjectToBoolean(args[2]);
}
auto& vocbase = GetContextVocBase(isolate);
auto ctx = transaction::V8Context::Create(vocbase, false);
GraphManager gmngr{vocbase};
auto graph = gmngr.lookupGraphByName(graphName);
if (graph.fail()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(graph.errorNumber(), graph.errorMessage());
}
TRI_ASSERT(graph.get() != nullptr);
GraphOperations gops{*(graph.get()), vocbase};
VPackBuilder builder;
builder.openObject();
builder.add("collection", VPackValue(vertexName));
builder.close();
OperationResult r =
gops.addOrphanCollection(builder.slice(), false, createCollection);
if (r.fail()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(r.errorNumber(), r.errorMessage());
}
graph = gmngr.lookupGraphByName(graphName);
if (graph.fail()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(graph.errorNumber(), graph.errorMessage());
}
TRI_ASSERT(graph.get() != nullptr);
VPackBuilder result;
result.openObject();
graph.get()->graphForClient(result);
result.close();
TRI_V8_RETURN(TRI_VPackToV8(isolate, result.slice()));
TRI_V8_TRY_CATCH_END
}
static void JS_DropEdgeDefinition(
v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
if (args.Length() < 2) {
TRI_V8_THROW_EXCEPTION_USAGE(
"_deleteEdgeDefinition(edgeCollection, dropCollection)");
}
if (!args[0]->IsString()) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_GRAPH_CREATE_MISSING_NAME);
}
if (!args[1]->IsString()) {
TRI_V8_THROW_EXCEPTION_USAGE(
"_deleteEdgeDefinition(edgeCollection, dropCollection)");
}
std::string graphName = TRI_ObjectToString(args[0]);
std::string edgeDefinitionName = TRI_ObjectToString(args[1]);
if (graphName.empty()) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_GRAPH_CREATE_MISSING_NAME);
}
if (edgeDefinitionName.empty()) {
TRI_V8_THROW_EXCEPTION_USAGE(
"_deleteEdgeDefinition(edgeCollection, dropCollection)");
}
bool dropCollections = false;
if (args.Length() >= 3) {
dropCollections = TRI_ObjectToBoolean(args[2]);
}
auto& vocbase = GetContextVocBase(isolate);
GraphManager gmngr{vocbase};
auto graph = gmngr.lookupGraphByName(graphName);
if (graph.fail()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(graph.errorNumber(), graph.errorMessage());
}
TRI_ASSERT(graph.get() != nullptr);
GraphOperations gops{*(graph.get()), vocbase};
OperationResult r =
gops.eraseEdgeDefinition(false, edgeDefinitionName, dropCollections);
if (r.fail()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(r.errorNumber(), r.errorMessage());
}
graph = gmngr.lookupGraphByName(graphName);
if (graph.fail()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(graph.errorNumber(), graph.errorMessage());
}
TRI_ASSERT(graph.get() != nullptr);
VPackBuilder result;
result.openObject();
graph.get()->graphForClient(result);
result.close();
TRI_V8_RETURN(TRI_VPackToV8(isolate, result.slice()));
TRI_V8_TRY_CATCH_END
}
static void InitV8GeneralGraphClass(v8::Handle<v8::Context> context,
TRI_vocbase_t* vocbase, TRI_v8_global_t* v8g,
v8::Isolate* isolate) {
/* FULL API
* _edgeCollections
* _vertexCollections(bool excludeOrphans)
* _EDGES
* _INEDGES
* _OUTEDGES
* _edges
* _vertices
* _fromVertex(edgeId)
* _toVertex(edgeId)
* _getEdgeCollectionByName
* _getVertexCollectionByName
* _neighbors
* _commonNeighbors
* _countCommonNeighbors
* _commonProperties
* _countCommonProperties
* _paths
* _shortestPath
* _distanceTo
* _absoluteEccentricity
* _farness
* _absoluteCloseness
* _eccentricity
* _closeness
* _absoluteBetweenness
* _betweenness
* _radius
* _diameter
* _orphanCollections
* _renameVertexCollection
* _getConnectingEdges
*/
v8::Handle<v8::ObjectTemplate> rt;
v8::Handle<v8::FunctionTemplate> ft;
ft = v8::FunctionTemplate::New(isolate);
ft->SetClassName(TRI_V8_ASCII_STRING(isolate, "ArangoGraph"));
rt = ft->InstanceTemplate();
rt->SetInternalFieldCount(2);
TRI_AddMethodVocbase(isolate, rt,
TRI_V8_ASCII_STRING(isolate, "_addVertexCollection"),
JS_AddVertexCollection);
TRI_AddMethodVocbase(isolate, rt,
TRI_V8_ASCII_STRING(isolate, "_deleteEdgeDefinition"),
JS_DropEdgeDefinition);
TRI_AddMethodVocbase(isolate, rt,
TRI_V8_ASCII_STRING(isolate, "_editEdgeDefinitions"),
JS_EditEdgeDefinitions);
TRI_AddMethodVocbase(isolate, rt,
TRI_V8_ASCII_STRING(isolate, "_extendEdgeDefinitions"),
JS_AddEdgeDefinitions);
TRI_AddMethodVocbase(isolate, rt,
TRI_V8_ASCII_STRING(isolate, "_removeVertexCollection"),
JS_RemoveVertexCollection);
v8g->GeneralGraphTempl.Reset(isolate, rt);
ft->SetClassName(TRI_V8_ASCII_STRING(isolate, "ArangoGraphCtor"));
TRI_AddGlobalFunctionVocbase(
isolate, TRI_V8_ASCII_STRING(isolate, "ArangoGraphCtor"),
ft->GetFunction(), true);
// TODO WE DO NOT NEED THIS. Update _create to return a graph object properly
// register the global object
v8::Handle<v8::Object> aa = rt->NewInstance();
if (!aa.IsEmpty()) {
TRI_AddGlobalVariableVocbase(
isolate, TRI_V8_ASCII_STRING(isolate, "ArangoGraph"), aa);
}
}
#ifdef USE_ENTERPRISE
static void InitV8SmartGraphClass(v8::Handle<v8::Context> context,
TRI_vocbase_t* vocbase, TRI_v8_global_t* v8g,
v8::Isolate* isolate) {
v8::Handle<v8::ObjectTemplate> rt;
v8::Handle<v8::FunctionTemplate> ft;
ft = v8::FunctionTemplate::New(isolate);
ft->SetClassName(TRI_V8_ASCII_STRING(isolate, "ArangoSmartGraph"));
rt = ft->InstanceTemplate();
rt->SetInternalFieldCount(2);
TRI_AddMethodVocbase(isolate, rt,
TRI_V8_ASCII_STRING(isolate, "_addVertexCollection"),
JS_AddVertexCollection);
TRI_AddMethodVocbase(isolate, rt,
TRI_V8_ASCII_STRING(isolate, "_deleteEdgeDefinition"),
JS_DropEdgeDefinition);
TRI_AddMethodVocbase(isolate, rt,
TRI_V8_ASCII_STRING(isolate, "_editEdgeDefinitions"),
JS_EditEdgeDefinitions);
TRI_AddMethodVocbase(isolate, rt,
TRI_V8_ASCII_STRING(isolate, "_extendEdgeDefinitions"),
JS_AddEdgeDefinitions);
TRI_AddMethodVocbase(isolate, rt,
TRI_V8_ASCII_STRING(isolate, "_removeVertexCollection"),
JS_RemoveVertexCollection);
v8g->SmartGraphTempl.Reset(isolate, rt);
ft->SetClassName(TRI_V8_ASCII_STRING(isolate, "ArangoSmartGraphCtor"));
TRI_AddGlobalFunctionVocbase(
isolate, TRI_V8_ASCII_STRING(isolate, "ArangoSmartGraphCtor"),
ft->GetFunction(), true);
// register the global object
v8::Handle<v8::Object> aa = rt->NewInstance();
if (!aa.IsEmpty()) {
TRI_AddGlobalVariableVocbase(
isolate, TRI_V8_ASCII_STRING(isolate, "ArangoSmartGraph"), aa);
}
}
#endif
static void InitV8GeneralGraphModule(v8::Handle<v8::Context> context,
TRI_vocbase_t* vocbase, TRI_v8_global_t* v8g,
v8::Isolate* isolate) {
/* These functions still have a JS only implementation
* JS ONLY:
* _edgeDefinitions
* _extendEdgeDefinitions
* _relation
* _registerCompatibilityFunctions
*/
/* TODO
* _create // SG => Potentially returns SG
* _graph // SG => Potentially returns SG
*/
v8::Handle<v8::ObjectTemplate> rt;
v8::Handle<v8::FunctionTemplate> ft;
ft = v8::FunctionTemplate::New(isolate);
ft->SetClassName(TRI_V8_ASCII_STRING(isolate, "ArangoGeneralGraphModule"));
rt = ft->InstanceTemplate();
rt->SetInternalFieldCount(0);
TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING(isolate, "_create"),
JS_CreateGraph);
TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING(isolate, "_drop"),
JS_DropGraph);
TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING(isolate, "_exists"),
JS_GraphExists);
TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING(isolate, "_graph"),
JS_GetGraph);
TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING(isolate, "_list"),
JS_GetGraphKeys);
TRI_AddMethodVocbase(
isolate, rt, TRI_V8_ASCII_STRING(isolate, "_listObjects"), JS_GetGraphs);
TRI_AddMethodVocbase(isolate, rt,
TRI_V8_ASCII_STRING(isolate, "_renameCollection"),
JS_RenameGraphCollection);
v8g->GeneralGraphModuleTempl.Reset(isolate, rt);
ft->SetClassName(TRI_V8_ASCII_STRING(isolate, "ArangoGeneralGraphModuleCtor"));
TRI_AddGlobalFunctionVocbase(
isolate, TRI_V8_ASCII_STRING(isolate, "ArangoGeneralGraphModuleCtor"),
ft->GetFunction(), true);
// register the global object
v8::Handle<v8::Object> aa = rt->NewInstance();
if (!aa.IsEmpty()) {
TRI_AddGlobalVariableVocbase(
isolate, TRI_V8_ASCII_STRING(isolate, "ArangoGeneralGraphModule"), aa);
}
}
void TRI_InitV8GeneralGraph(v8::Handle<v8::Context> context,
TRI_vocbase_t* vocbase, TRI_v8_global_t* v8g,
v8::Isolate* isolate) {
InitV8GeneralGraphModule(context, vocbase, v8g, isolate);
InitV8GeneralGraphClass(context, vocbase, v8g, isolate);
#ifdef USE_ENTERPRISE
InitV8SmartGraphClass(context, vocbase, v8g, isolate);
#endif
}

View File

@ -18,32 +18,19 @@
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Michael Hackstein
/// @author Heiko Kernbach
////////////////////////////////////////////////////////////////////////////////
#ifndef ARANGOD_VOCBASE_GRAPHS_H
#define ARANGOD_VOCBASE_GRAPHS_H 1
#ifndef ARANGOD_V8_SERVER_V8_GENERAL_GRAPH_H
#define ARANGOD_V8_SERVER_V8_GENERAL_GRAPH_H 1
#include "VocBase/vocbase.h"
#include <v8.h>
namespace arangodb {
namespace aql {
class Graph;
}
struct TRI_vocbase_t;
struct TRI_v8_global_t;
namespace transaction {
class Context;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get an instance of Graph by Name.
/// returns nullptr if graph is not existing
/// The caller has to take care for the memory.
////////////////////////////////////////////////////////////////////////////////
arangodb::aql::Graph* lookupGraphByName(std::shared_ptr<transaction::Context>, std::string const& name);
} // namespace arangodb
void TRI_InitV8GeneralGraph(v8::Handle<v8::Context> context,
TRI_vocbase_t* vocbase, TRI_v8_global_t* v8g,
v8::Isolate* isolate);
#endif

View File

@ -79,6 +79,7 @@
#include "V8Server/v8-views.h"
#include "V8Server/v8-voccursor.h"
#include "V8Server/v8-vocindex.h"
#include "V8Server/v8-general-graph.h"
#include "VocBase/KeyGenerator.h"
#include "VocBase/LogicalCollection.h"
#include "VocBase/Methods/Databases.h"
@ -2068,6 +2069,7 @@ void TRI_InitV8VocBridge(
TRI_InitV8Collections(context, &vocbase, v8g, isolate, ArangoNS);
TRI_InitV8Views(context, &vocbase, v8g, isolate, ArangoNS);
TRI_InitV8Users(context, &vocbase, v8g, isolate);
TRI_InitV8GeneralGraph(context, &vocbase, v8g, isolate);
TRI_InitV8cursor(context, v8g);

View File

@ -1,86 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 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
////////////////////////////////////////////////////////////////////////////////
#include "Graphs.h"
#include "Aql/Graphs.h"
#include "Basics/StaticStrings.h"
#include "Cluster/ClusterMethods.h"
#include "Utils/OperationOptions.h"
#include "Utils/SingleCollectionTransaction.h"
#include "Transaction/Context.h"
#include <sstream>
using namespace arangodb;
#ifndef USE_ENTERPRISE
std::string const GRAPHS = "_graphs";
////////////////////////////////////////////////////////////////////////////////
/// @brief Load a graph from the _graphs collection; local and coordinator way
////////////////////////////////////////////////////////////////////////////////
arangodb::aql::Graph* arangodb::lookupGraphByName(std::shared_ptr<transaction::Context> transactionContext,
std::string const& name) {
SingleCollectionTransaction trx(transactionContext, GRAPHS, AccessMode::Type::READ);
Result res = trx.begin();
if (!res.ok()) {
std::stringstream ss;
ss << "while looking up graph '" << name << "': " << res.errorMessage();
res.reset(res.errorNumber(), ss.str());
THROW_ARANGO_EXCEPTION(res);
}
VPackBuilder b;
{
VPackObjectBuilder guard(&b);
b.add(StaticStrings::KeyString, VPackValue(name));
}
// Default options are enough here
OperationOptions options;
OperationResult result = trx.document(GRAPHS, b.slice(), options);
// Commit or abort.
res = trx.finish(result.result);
if (result.fail()) {
THROW_ARANGO_EXCEPTION_FORMAT(result.errorNumber(), "while looking up graph '%s'",
name.c_str());
}
if (res.fail()) {
std::stringstream ss;
ss << "while looking up graph '" << name << "': " << res.errorMessage();
res.reset(res.errorNumber(), ss.str());
THROW_ARANGO_EXCEPTION(res);
}
VPackSlice info = result.slice();
if (info.isExternal()) {
info = info.resolveExternal();
}
return new arangodb::aql::Graph(info);
}
#endif

View File

@ -445,7 +445,7 @@ static int RenameGraphCollections(TRI_vocbase_t* vocbase,
}
StringBuffer buffer(true);
buffer.appendText("require('@arangodb/general-graph')._renameCollection(");
buffer.appendText("require('@arangodb/general-graph-common')._renameCollection(");
buffer.appendJsonEncoded(oldName.c_str(), oldName.size());
buffer.appendChar(',');
buffer.appendJsonEncoded(newName.c_str(), newName.size());

View File

@ -655,7 +655,7 @@
} else {
newCollectionObject.isSmart = true;
newCollectionObject.options = {
numberOfShards: $('#new-numberOfShards').val(),
numberOfShards: parseInt($('#new-numberOfShards').val()),
smartGraphAttribute: $('#new-smartGraphAttribute').val(),
replicationFactor: parseInt($('#new-replicationFactor').val())
};
@ -664,15 +664,15 @@
if (frontendConfig.isCluster) {
if ($('#general-numberOfShards').val().length > 0) {
newCollectionObject.options = {
numberOfShards: $('#general-numberOfShards').val()
numberOfShards: parseInt($('#general-numberOfShards').val())
};
}
if ($('#general-replicationFactor').val().length > 0) {
if (newCollectionObject.options) {
newCollectionObject.options.replicationFactor = $('#general-replicationFactor').val();
newCollectionObject.options.replicationFactor = parseInt($('#general-replicationFactor').val());
} else {
newCollectionObject.options = {
replicationFactor: $('#general-replicationFactor').val()
replicationFactor: parseInt($('#general-replicationFactor').val())
};
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +0,0 @@
{
"name": "gharial",
"description": "ArangoDB Graph Module",
"author": "ArangoDB GmbH",
"version": "3.0.0",
"license": "Apache-2.0",
"engines": {
"arangodb": "^3.0.0-0 || ^3.0.0"
},
"repository": {
"type": "git",
"url": "https://github.com/arangodb/arangodb.git"
},
"contributors": [
{
"name": "Michael Hackstein",
"email": "m.hackstein@arangodb.com"
}
],
"main": "gharial.js"
}

View File

@ -0,0 +1,205 @@
'use strict';
// //////////////////////////////////////////////////////////////////////////////
// / @brief Replication management
// /
// / @file
// /
// / DISCLAIMER
// /
// / Copyright 2012 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 triAGENS GmbH, Cologne, Germany
// /
// / @author Heiko Kernbach
// / @author Copyright 2018, ArangoDB GmbH, Cologne, Germany
// //////////////////////////////////////////////////////////////////////////////
const internal = require('internal');
const arangosh = require('@arangodb/arangosh');
const ggc = require('@arangodb/general-graph-common');
const _ = require('lodash');
const db = internal.db;
const GRAPH_PREFIX = '_api/gharial/';
// remove me later
exports._exists = ggc._exists;
// TODO There are several db._flushCache() calls here, whenever gharial might
// have added or dropped collections. Maybe this should be called even when
// the request has failed (in which case now it isn't, because an exception is
// thrown first).
exports._listObjects = function () {
const uri = GRAPH_PREFIX;
const requestResult = arangosh.checkRequestResult(db._connection.GET(uri));
return requestResult.graphs;
};
exports._list = function () {
const uri = GRAPH_PREFIX;
const requestResult = arangosh.checkRequestResult(db._connection.GET(uri));
const graphs = requestResult.graphs;
const result = [];
_.each(graphs, function (graph) {
result.push(graph._key);
});
return result;
};
// inherited graph class
const CommonGraph = ggc.__GraphClass;
CommonGraph.prototype.__updateDefinitions = function (edgeDefs, orphans) {
this.__edgeDefinitions = edgeDefs;
this.__orphanCollections = orphans;
};
CommonGraph.prototype._extendEdgeDefinitions = function (edgeDefinition) {
const data = edgeDefinition || {};
const uri = GRAPH_PREFIX + encodeURIComponent(this.__name) + "/edge";
const requestResult = arangosh.checkRequestResult(db._connection.POST(uri, JSON.stringify(data)));
const graph = requestResult.graph;
try {
this.__updateDefinitions(graph.edgeDefinitions, graph.orphanCollections);
} catch (ignore) {
}
};
CommonGraph.prototype._editEdgeDefinitions = function (edgeDefinition) {
const data = edgeDefinition || {};
const uri = GRAPH_PREFIX + encodeURIComponent(this.__name) + "/edge/" + edgeDefinition.collection;
const requestResult = arangosh.checkRequestResult(db._connection.PUT(uri, JSON.stringify(data)));
const graph = requestResult.graph;
try {
this.__updateDefinitions(graph.edgeDefinitions, graph.orphanCollections);
} catch (ignore) {
}
};
CommonGraph.prototype._addVertexCollection = function (name, createCollection) {
const data = {};
if (name) {
data.collection = name;
}
let uri = GRAPH_PREFIX + encodeURIComponent(this.__name) + "/vertex";
if (createCollection !== false) {
uri += "?createCollection=true";
} else {
uri += "?createCollection=false";
}
const requestResult = arangosh.checkRequestResult(db._connection.POST(uri, JSON.stringify(data)));
const graph = requestResult.graph;
try {
this.__updateDefinitions(graph.edgeDefinitions, graph.orphanCollections);
} catch (ignore) {
}
if (createCollection !== false) {
db._flushCache();
}
};
CommonGraph.prototype._removeVertexCollection = function (name, dropCollection) {
let uri = GRAPH_PREFIX + encodeURIComponent(this.__name) + "/vertex/" + encodeURIComponent(name);
if (dropCollection === true) {
uri += "?dropCollections=true";
} else {
uri += "?dropCollections=false";
}
const requestResult = arangosh.checkRequestResult(db._connection.DELETE(uri));
const graph = requestResult.graph;
try {
this.__updateDefinitions(graph.edgeDefinitions, graph.orphanCollections);
} catch (ignore) {
}
if (dropCollection === true) {
db._flushCache();
}
};
CommonGraph.prototype._deleteEdgeDefinition = function (name, dropCollection = false) {
let uri = GRAPH_PREFIX + encodeURIComponent(this.__name) + "/edge/" + encodeURIComponent(name);
if (dropCollection === true) {
uri += "?dropCollections=true";
} else {
uri += "?dropCollections=false";
}
const requestResult = arangosh.checkRequestResult(db._connection.DELETE(uri));
const graph = requestResult.graph;
try {
this.__updateDefinitions(graph.edgeDefinitions, graph.orphanCollections);
} catch (ignore) {
}
if (dropCollection === true) {
db._flushCache();
}
};
exports._graph = function (graphName) {
const uri = GRAPH_PREFIX + encodeURIComponent(graphName);
const requestResult = arangosh.checkRequestResult(db._connection.GET(uri));
return new CommonGraph(requestResult.graph);
};
exports._create = function (name, edgeDefinitions, orphans, options) {
const data = {};
if (name) {
data.name = name;
}
if (edgeDefinitions) {
data.edgeDefinitions = edgeDefinitions;
}
if (orphans) {
data.orphanCollections = orphans;
}
if (options) {
data.options = options;
}
const uri = GRAPH_PREFIX;
const requestResult = arangosh.checkRequestResult(db._connection.POST(uri, JSON.stringify(data)));
db._flushCache();
return new CommonGraph(requestResult.graph);
};
exports._drop = function (graphName, dropCollections) {
let uri = GRAPH_PREFIX + encodeURIComponent(graphName);
if (dropCollections) {
uri += "?dropCollections=true";
}
const requestResult = arangosh.checkRequestResult(db._connection.DELETE(uri));
if (dropCollections) {
db._flushCache();
}
return requestResult.result;
};
// js based helper functions
exports.__GraphClass = CommonGraph;
exports._edgeDefinitions = ggc._edgeDefinitions;
exports._extendEdgeDefinitions = ggc._extendEdgeDefinitions;
exports._relation = ggc._relation;
exports._registerCompatibilityFunctions = ggc._registerCompatibilityFunctions;

View File

@ -24,15 +24,17 @@
// / @author Michael Hackstein
// //////////////////////////////////////////////////////////////////////////////
const expect = require('chai').expect;
const chai = require('chai');
const expect = chai.expect;
chai.Assertion.addProperty('does', function () { return this; });
const arangodb = require('@arangodb');
const request = require('@arangodb/request');
const ERRORS = arangodb.errors;
const db = arangodb.db;
const wait = require('internal').wait;
const extend = require('lodash').extend;
const internal = require('internal');
const wait = internal.wait;
describe('_api/gharial', () => {
@ -314,4 +316,296 @@ describe('_api/gharial', () => {
expect(db._collection(vName)).to.not.be.null;
});
it('should check if edges can only be created if their _from and _to vertices are existent - should NOT create - invalid from', () => {
const examples = require('@arangodb/graph-examples/example-graph');
const exampleGraphName = 'knows_graph';
const vName = 'persons';
const eName = 'knows';
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'peter',
_to: 'persons/charlie'
};
let req = request.post(url + '/' + exampleGraphName + '/edge/knows', {
body: JSON.stringify(edgeDef)
});
expect(req.statusCode).to.equal(400);
expect(req.json.errorNum).to.equal(ERRORS.ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('should check if edges can only be created if their _from and _to vertices are existent - should NOT create - invalid to', () => {
const examples = require('@arangodb/graph-examples/example-graph');
const exampleGraphName = 'knows_graph';
const vName = 'persons';
const eName = 'knows';
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'persons/peter',
_to: 'charlie'
};
let req = request.post(url + '/' + exampleGraphName + '/edge/knows', {
body: JSON.stringify(edgeDef)
});
expect(req.statusCode).to.equal(400);
expect(req.json.errorNum).to.equal(ERRORS.ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('should check if edges can only be created if their _from and _to vertices are existent - should NOT create - invalid from and to attributes', () => {
const examples = require('@arangodb/graph-examples/example-graph');
const exampleGraphName = 'knows_graph';
const vName = 'persons';
const eName = 'knows';
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'peter',
_to: 'charlie'
};
let req = request.post(url + '/' + exampleGraphName + '/edge/knows', {
body: JSON.stringify(edgeDef)
});
expect(req.statusCode).to.equal(400);
expect(req.json.errorNum).to.equal(ERRORS.ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('should check if edges can only be created if their _from and _to vertices are existent - should NOT create - missing from and to attributes', () => {
const examples = require('@arangodb/graph-examples/example-graph');
const exampleGraphName = 'knows_graph';
const vName = 'persons';
const eName = 'knows';
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
};
let req = request.post(url + '/' + exampleGraphName + '/edge/knows', {
body: JSON.stringify(edgeDef)
});
expect(req.statusCode).to.equal(400);
expect(req.json.errorNum).to.equal(ERRORS.ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('should check if incident edges are deleted with a vertex', () => {
const examples = require('@arangodb/graph-examples/example-graph');
const exampleGraphName = 'knows_graph';
const vName = 'persons';
const eName = 'knows';
// vertices
const alice = 'alice';
const bob = 'bob';
const eve = 'eve';
expect(db._collection(eName)).to.be.null;
expect(db._collection(vName)).to.be.null;
// load graph
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
// pre-check that the expected edges are there
expect(db[eName].all().toArray().length).to.equal(5);
// delete vertex bob
const res = request.delete(
`${url}/${exampleGraphName}/vertex/${vName}/${bob}`
);
// check response
expect(res).to.be.an.instanceof(request.Response);
expect(res.body).to.be.a('string');
const body = JSON.parse(res.body);
// 202 without waitForSync (default)
expect(body).to.eql({
error: false,
code: 202,
removed: true
});
// check that all edges incident to bob were removed as well
expect(db[eName].all().toArray().length).to.equal(1);
// check that the remaining edge is the expected one
const eveKnowsAlice = db[eName].all().toArray()[0];
expect(eveKnowsAlice).to.have.all.keys(
['_key', '_id', '_rev', '_from', '_to', 'vertex']
);
expect(eveKnowsAlice).to.include({
_from: `${vName}/${eve}`,
_to: `${vName}/${alice}`,
vertex: eve
});
});
it('should check that non-graph incident edges are not deleted with a' +
' vertex', () => {
const examples = require('@arangodb/graph-examples/example-graph');
const exampleGraphName = 'knows_graph';
const vName = 'persons';
const eName = 'knows';
// vertices
const alice = 'alice';
const bob = 'bob';
const charlie = 'charlie';
const dave = 'dave';
const eve = 'eve';
expect(db._collection(eName)).to.be.null;
expect(db._collection(vName)).to.be.null;
// load graph
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
const ngEdges = db._create(eColName);
ngEdges.insert({
_from: `${vName}/${bob}`,
_to: `${vName}/${charlie}`,
name: 'bob->charlie'
});
ngEdges.insert({
_from: `${vName}/${dave}`,
_to: `${vName}/${bob}`,
name: 'dave->bob'
});
// pre-check that the expected edges are there
expect(db[eName].all().toArray().length).to.equal(5);
// delete vertex bob
const res = request.delete(
`${url}/${exampleGraphName}/vertex/${vName}/${bob}`
);
// check response
expect(res).to.be.an.instanceof(request.Response);
expect(res.body).to.be.a('string');
const body = JSON.parse(res.body);
// 202 without waitForSync (default)
expect(body).to.eql({
error: false,
code: 202,
removed: true
});
// check that the edges outside of g are still there
let remainingEdges = ngEdges.all().toArray();
expect(remainingEdges.length).to.equal(2);
expect(remainingEdges.map(x => x.name))
.to.have.members(['bob->charlie', 'dave->bob']);
});
// TODO deleting a vertex via the graph api should probably delete all
// edges that are in any graph's edge collection, not only the "current"
// graph. Decide this and write a test.
it('should check that edges can be replaced', () => {
const examples = require('@arangodb/graph-examples/example-graph');
const exampleGraphName = 'knows_graph';
const vName = 'persons';
const eName = 'knows';
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
const e = db.knows.any();
const newEdge = Object.assign({}, e);
newEdge.newAttribute = 'new value';
const res = request.put(
`${url}/${exampleGraphName}/edge/${eName}/${e._key}`,
{body: JSON.stringify(newEdge)}
);
// 202 without waitForSync (default)
expect(res.statusCode).to.equal(202);
expect(res.json.code).to.equal(202);
expect(res.json.error).to.equal(false);
expect(res.json.edge._key).to.equal(e._key);
expect(db.knows.document(e._key))
.to.be.an('object')
.that.has.property('newAttribute')
.which.equals('new value');
});
it('should check that edges can NOT be replaced if their _from or _to' +
' attribute is missing', () => {
const examples = require('@arangodb/graph-examples/example-graph');
const exampleGraphName = 'knows_graph';
const vName = 'persons';
const eName = 'knows';
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
const e = db.knows.any();
let newEdge;
let newEdges = {};
newEdge = newEdges['_from missing'] = Object.assign({new: "new"}, e);
delete newEdge._from;
newEdge = newEdges['_to missing'] = Object.assign({new: "new"}, e);
delete newEdge._to;
newEdge = newEdges['_from and _to missing'] = Object.assign({new: "new"}, e);
delete newEdge._from;
delete newEdge._to;
for (let key in newEdges) {
if (!newEdges.hasOwnProperty(key)) {
continue;
}
const description = key;
const newEdge = newEdges[key];
const res = request.put(
`${url}/${exampleGraphName}/edge/${eName}/${e._key}`,
{body: JSON.stringify(newEdge)}
);
expect(res.statusCode, description).to.equal(400);
expect(res.json.errorNum, description)
.to.equal(ERRORS.ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE.code);
}
expect(db.knows.document(e._key))
.to.be.an('object')
.that.does.not.have.property('new');
});
});

View File

@ -286,6 +286,10 @@
"ERROR_GRAPH_COLLECTION_USED_IN_ORPHANS" : { "code" : 1938, "message" : "collection used in orphans" },
"ERROR_GRAPH_EDGE_COL_DOES_NOT_EXIST" : { "code" : 1939, "message" : "edge collection does not exist or is not part of the graph" },
"ERROR_GRAPH_EMPTY" : { "code" : 1940, "message" : "empty graph" },
"ERROR_GRAPH_INTERNAL_DATA_CORRUPT" : { "code" : 1941, "message" : "internal graph data corrupt" },
"ERROR_GRAPH_INTERNAL_EDGE_COLLECTION_ALREADY_SET" : { "code" : 1942, "message" : "edge collection already set" },
"ERROR_GRAPH_CREATE_MALFORMED_ORPHAN_LIST" : { "code" : 1943, "message" : "malformed orphan list" },
"ERROR_GRAPH_EDGE_DEFINITION_IS_DOCUMENT" : { "code" : 1944, "message" : "edge definition collection is a document collection" },
"ERROR_SESSION_UNKNOWN" : { "code" : 1950, "message" : "unknown session" },
"ERROR_SESSION_EXPIRED" : { "code" : 1951, "message" : "session expired" },
"SIMPLE_CLIENT_UNKNOWN_ERROR" : { "code" : 2000, "message" : "unknown client error" },
@ -316,6 +320,7 @@
"ERROR_CANNOT_DROP_SMART_COLLECTION" : { "code" : 4002, "message" : "cannot drop this smart collection" },
"ERROR_KEY_MUST_BE_PREFIXED_WITH_SMART_GRAPH_ATTRIBUTE" : { "code" : 4003, "message" : "in smart vertex collections _key must be prefixed with the value of the smart graph attribute" },
"ERROR_ILLEGAL_SMART_GRAPH_ATTRIBUTE" : { "code" : 4004, "message" : "attribute cannot be used as smart graph attribute" },
"ERROR_SMART_GRAPH_ATTRIBUTE_MISMATCH" : { "code" : 4005, "message" : "smart graph attribute mismatch" },
"ERROR_CLUSTER_REPAIRS_FAILED" : { "code" : 5000, "message" : "error during cluster repairs" },
"ERROR_CLUSTER_REPAIRS_NOT_ENOUGH_HEALTHY" : { "code" : 5001, "message" : "not enough (healthy) db servers" },
"ERROR_CLUSTER_REPAIRS_REPLICATION_FACTOR_VIOLATED" : { "code" : 5002, "message" : "replication factor violated during cluster repairs" },

View File

@ -81,17 +81,9 @@ var findOrCreateCollectionByName = function (name, type, noCreate, options) {
let res = false;
if (col === null && !noCreate) {
if (type === ArangoCollection.TYPE_DOCUMENT) {
if (options) {
col = db._create(name, options);
} else {
col = db._create(name);
}
col = db._create(name, options);
} else {
if (options) {
col = db._createEdgeCollection(name, options);
} else {
col = db._createEdgeCollection(name);
}
col = db._createEdgeCollection(name, options);
}
res = true;
} else if (!(col instanceof ArangoCollection)) {
@ -320,12 +312,20 @@ var transformExampleToAQL = function (examples, collections, bindVars, varname)
// / internal helper to sort a graph's edge definitions
// //////////////////////////////////////////////////////////////////////////////
var sortEdgeDefinition = function (edgeDefinition) {
edgeDefinition.from = edgeDefinition.from.sort();
edgeDefinition.to = edgeDefinition.to.sort();
var sortEdgeDefinitionInplace = function (edgeDefinition) {
edgeDefinition.from.sort();
edgeDefinition.to.sort();
return edgeDefinition;
};
var sortEdgeDefinition = function (edgeDefinition) {
return {
collection: edgeDefinition.collection,
from: edgeDefinition.from.slice().sort(),
to: edgeDefinition.to.slice().sort(),
};
};
// //////////////////////////////////////////////////////////////////////////////
// / @brief create a new graph
// //////////////////////////////////////////////////////////////////////////////
@ -731,6 +731,13 @@ var checkIfMayBeDropped = function (colName, graphName, graphs) {
return result;
};
const edgeDefinitionsEqual = function (leftEdgeDef, rightEdgeDef) {
leftEdgeDef = sortEdgeDefinition(leftEdgeDef);
rightEdgeDef = sortEdgeDefinition(rightEdgeDef);
const stringify = obj => JSON.stringify(obj, Object.keys(obj).sort());
return stringify(leftEdgeDef) === stringify(rightEdgeDef);
};
// @brief Class Graph. Defines a graph in the Database.
class Graph {
constructor (info) {
@ -762,7 +769,7 @@ class Graph {
var self = this;
// Create Hidden Properties
createHiddenProperty(this, '__useBuiltIn', useBuiltIn);
createHiddenProperty(this, '__name', info._key);
createHiddenProperty(this, '__name', info._key || info.name);
createHiddenProperty(this, '__vertexCollections', vertexCollections);
createHiddenProperty(this, '__edgeCollections', edgeCollections);
createHiddenProperty(this, '__edgeDefinitions', info.edgeDefinitions);
@ -779,7 +786,7 @@ class Graph {
// Create Hidden Functions
createHiddenProperty(this, '__updateBindCollections', updateBindCollections);
createHiddenProperty(this, '__sortEdgeDefinition', sortEdgeDefinition);
createHiddenProperty(this, '__sortEdgeDefinition', sortEdgeDefinitionInplace);
updateBindCollections(self);
}
@ -1551,226 +1558,6 @@ class Graph {
return result;
}
// //////////////////////////////////////////////////////////////////////////////
// / @brief was docuBlock JSF_general_graph__extendEdgeDefinitions
// //////////////////////////////////////////////////////////////////////////////
_extendEdgeDefinitions (edgeDefinition) {
edgeDefinition = sortEdgeDefinition(edgeDefinition);
var self = this;
var err;
// check if edgeCollection not already used
var eC = edgeDefinition.collection;
// ... in same graph
if (this.__edgeCollections[eC] !== undefined) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_COLLECTION_MULTI_USE.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_COLLECTION_MULTI_USE.message;
throw err;
}
// in different graph
db._graphs.toArray().forEach(
function (singleGraph) {
var sGEDs = singleGraph.edgeDefinitions;
sGEDs.forEach(
function (sGED) {
var col = sGED.collection;
if (col === eC) {
if (JSON.stringify(sGED) !== JSON.stringify(edgeDefinition)) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_COLLECTION_USE_IN_MULTI_GRAPHS.code;
err.errorMessage = col + ' ' +
arangodb.errors.ERROR_GRAPH_COLLECTION_USE_IN_MULTI_GRAPHS.message;
throw err;
}
}
}
);
}
);
findOrCreateCollectionsByEdgeDefinitions([edgeDefinition]);
this.__edgeDefinitions.push(edgeDefinition);
db._graphs.update(this.__name, {edgeDefinitions: this.__edgeDefinitions});
this.__edgeCollections[edgeDefinition.collection] = db[edgeDefinition.collection];
edgeDefinition.from.forEach(
function (vc) {
self[vc] = db[vc];
// remove from __orphanCollections
var orphanIndex = self.__orphanCollections.indexOf(vc);
if (orphanIndex !== -1) {
self.__orphanCollections.splice(orphanIndex, 1);
}
// push into __vertexCollections
if (self.__vertexCollections[vc] === undefined) {
self.__vertexCollections[vc] = db[vc];
}
}
);
edgeDefinition.to.forEach(
function (vc) {
self[vc] = db[vc];
// remove from __orphanCollections
var orphanIndex = self.__orphanCollections.indexOf(vc);
if (orphanIndex !== -1) {
self.__orphanCollections.splice(orphanIndex, 1);
}
// push into __vertexCollections
if (self.__vertexCollections[vc] === undefined) {
self.__vertexCollections[vc] = db[vc];
}
}
);
updateBindCollections(this);
}
// //////////////////////////////////////////////////////////////////////////////
// / @brief was docuBlock JSF_general_graph__editEdgeDefinition
// //////////////////////////////////////////////////////////////////////////////
_editEdgeDefinitions (edgeDefinition) {
edgeDefinition = sortEdgeDefinition(edgeDefinition);
var self = this;
// check, if in graphs edge definition
if (this.__edgeCollections[edgeDefinition.collection] === undefined) {
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_EDGE_COLLECTION_NOT_USED.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_EDGE_COLLECTION_NOT_USED.message;
throw err;
}
findOrCreateCollectionsByEdgeDefinitions([edgeDefinition]);
// evaluate collections to add to orphanage
var possibleOrphans = [];
var currentEdgeDefinition;
this.__edgeDefinitions.forEach(
function (ed) {
if (edgeDefinition.collection === ed.collection) {
currentEdgeDefinition = ed;
}
}
);
var currentCollections = _.union(currentEdgeDefinition.from, currentEdgeDefinition.to);
var newCollections = _.union(edgeDefinition.from, edgeDefinition.to);
currentCollections.forEach(
function (colName) {
if (newCollections.indexOf(colName) === -1) {
possibleOrphans.push(colName);
}
}
);
// change definition for ALL graphs
var graphs = exports._listObjects();
graphs.forEach(
function (graph) {
changeEdgeDefinitionsForGraph(graph, edgeDefinition, newCollections, possibleOrphans, self);
}
);
updateBindCollections(this);
}
// //////////////////////////////////////////////////////////////////////////////
// / @brief was docuBlock JSF_general_graph__deleteEdgeDefinition
// //////////////////////////////////////////////////////////////////////////////
_deleteEdgeDefinition (edgeCollection, dropCollection) {
// check, if in graphs edge definition
if (this.__edgeCollections[edgeCollection] === undefined) {
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_EDGE_COLLECTION_NOT_USED.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_EDGE_COLLECTION_NOT_USED.message;
throw err;
}
if (dropCollection) {
checkRWPermission(edgeCollection);
}
let edgeDefinitions = this.__edgeDefinitions;
let self = this;
let usedVertexCollections = [];
let possibleOrphans = [];
let index;
edgeDefinitions.forEach(
function (edgeDefinition, idx) {
if (edgeDefinition.collection === edgeCollection) {
index = idx;
possibleOrphans = edgeDefinition.from;
possibleOrphans = _.union(possibleOrphans, edgeDefinition.to);
} else {
usedVertexCollections = _.union(usedVertexCollections, edgeDefinition.from);
usedVertexCollections = _.union(usedVertexCollections, edgeDefinition.to);
}
}
);
this.__edgeDefinitions.splice(index, 1);
possibleOrphans.forEach(
function (po) {
if (usedVertexCollections.indexOf(po) === -1) {
self.__orphanCollections.push(po);
}
}
);
updateBindCollections(this);
let gdb = getGraphCollection();
gdb.update(
this.__name,
{
orphanCollections: this.__orphanCollections,
edgeDefinitions: this.__edgeDefinitions
}
);
if (dropCollection) {
db._drop(edgeCollection);
}
}
// //////////////////////////////////////////////////////////////////////////////
// / @brief was docuBlock JSF_general_graph__addVertexCollection
// //////////////////////////////////////////////////////////////////////////////
_addVertexCollection (vertexCollectionName, createCollection) {
// check edgeCollection
var ec = db._collection(vertexCollectionName);
var err;
if (ec === null) {
if (createCollection !== false) {
db._create(vertexCollectionName);
} else {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.code;
err.errorMessage = vertexCollectionName + arangodb.errors.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.message;
throw err;
}
} else if (ec.type() !== 2) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_WRONG_COLLECTION_TYPE_VERTEX.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_WRONG_COLLECTION_TYPE_VERTEX.message;
throw err;
}
if (this.__vertexCollections[vertexCollectionName] !== undefined) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_COLLECTION_USED_IN_EDGE_DEF.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_COLLECTION_USED_IN_EDGE_DEF.message;
throw err;
}
if (_.includes(this.__orphanCollections, vertexCollectionName)) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_COLLECTION_USED_IN_ORPHANS.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_COLLECTION_USED_IN_ORPHANS.message;
throw err;
}
this.__orphanCollections.push(vertexCollectionName);
updateBindCollections(this);
db._graphs.update(this.__name, {orphanCollections: this.__orphanCollections});
}
// //////////////////////////////////////////////////////////////////////////////
// / @brief was docuBlock JSF_general_graph__orphanCollections
// //////////////////////////////////////////////////////////////////////////////
@ -2013,6 +1800,9 @@ exports._create = function (graphName, edgeDefinitions, orphanCollections, optio
err.errorMessage = arangodb.errors.ERROR_GRAPH_CREATE_MALFORMED_EDGE_DEFINITION.message;
throw err;
}
edgeDefinitions = _.cloneDeep(edgeDefinitions);
// check, if a collection is already used in a different edgeDefinition
let tmpCollections = [];
let tmpEdgeDefinitions = {};
@ -2039,7 +1829,7 @@ exports._create = function (graphName, edgeDefinitions, orphanCollections, optio
(sGED) => {
var col = sGED.collection;
if (tmpCollections.indexOf(col) !== -1) {
if (JSON.stringify(sGED) !== JSON.stringify(tmpEdgeDefinitions[col])) {
if (!edgeDefinitionsEqual(sGED, tmpEdgeDefinitions[col])) {
let err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_COLLECTION_USE_IN_MULTI_GRAPHS.code;
err.errorMessage = col + ' ' +
@ -2076,12 +1866,7 @@ exports._create = function (graphName, edgeDefinitions, orphanCollections, optio
}
);
edgeDefinitions.forEach(
(eD, index) => {
var tmp = sortEdgeDefinition(eD);
edgeDefinitions[index] = tmp;
}
);
edgeDefinitions.forEach(sortEdgeDefinitionInplace);
orphanCollections = orphanCollections.sort();
var data = gdb.save({
@ -2089,7 +1874,7 @@ exports._create = function (graphName, edgeDefinitions, orphanCollections, optio
'edgeDefinitions': edgeDefinitions,
'_key': graphName,
'numberOfShards': options.numberOfShards || 1,
'replicationFactor': options.replicationFactor || 1
'replicationFactor': options.replicationFactor || 1,
}, options);
data.orphanCollections = orphanCollections;
data.edgeDefinitions = edgeDefinitions;
@ -2257,82 +2042,82 @@ exports._listObjects = function () {
exports._registerCompatibilityFunctions = function () {
const aqlfunctions = require('@arangodb/aql/functions');
aqlfunctions.register('arangodb::GRAPH_EDGES', function (graphName, vertexExample, options) {
var gm = require('@arangodb/general-graph');
var gm = require('@arangodb/general-graph-common');
var g = gm._graph(graphName);
return g._edges(vertexExample, options);
}, false);
aqlfunctions.register('arangodb::GRAPH_VERTICES', function (graphName, vertexExample, options) {
var gm = require('@arangodb/general-graph');
var gm = require('@arangodb/general-graph-common');
var g = gm._graph(graphName);
return g._vertices(vertexExample, options);
}, false);
aqlfunctions.register('arangodb::GRAPH_NEIGHBORS', function (graphName, vertexExample, options) {
var gm = require('@arangodb/general-graph');
var gm = require('@arangodb/general-graph-common');
var g = gm._graph(graphName);
return g._neighbors(vertexExample, options);
}, false);
aqlfunctions.register('arangodb::GRAPH_COMMON_NEIGHBORS', function (graphName, vertex1Example, vertex2Example, options1, options2) {
var gm = require('@arangodb/general-graph');
var gm = require('@arangodb/general-graph-common');
var g = gm._graph(graphName);
return g._commonNeighbors(vertex1Example, vertex2Example, options1, options2);
}, false);
aqlfunctions.register('arangodb::GRAPH_COMMON_PROPERTIES', function (graphName, vertex1Example, vertex2Example, options) {
var gm = require('@arangodb/general-graph');
var gm = require('@arangodb/general-graph-common');
var g = gm._graph(graphName);
return g._commonProperties(vertex1Example, vertex2Example, options);
}, false);
aqlfunctions.register('arangodb::GRAPH_PATHS', function (graphName, options) {
var gm = require('@arangodb/general-graph');
var gm = require('@arangodb/general-graph-common');
var g = gm._graph(graphName);
return g._paths(options);
}, false);
aqlfunctions.register('arangodb::GRAPH_SHORTEST_PATH', function (graphName, startVertexExample, edgeVertexExample, options) {
var gm = require('@arangodb/general-graph');
var gm = require('@arangodb/general-graph-common');
var g = gm._graph(graphName);
return g._shortestPath(startVertexExample, edgeVertexExample, options);
}, false);
aqlfunctions.register('arangodb::GRAPH_DISTANCE_TO', function (graphName, startVertexExample, edgeVertexExample, options) {
var gm = require('@arangodb/general-graph');
var gm = require('@arangodb/general-graph-common');
var g = gm._graph(graphName);
return g._distanceTo(startVertexExample, edgeVertexExample, options);
}, false);
aqlfunctions.register('arangodb::GRAPH_ABSOLUTE_ECCENTRICITY', function (graphName, vertexExample, options) {
var gm = require('@arangodb/general-graph');
var gm = require('@arangodb/general-graph-common');
var g = gm._graph(graphName);
return g._absoluteEccentricity(vertexExample, options);
}, false);
aqlfunctions.register('arangodb::GRAPH_ECCENTRICITY', function (graphName, options) {
var gm = require('@arangodb/general-graph');
var gm = require('@arangodb/general-graph-common');
var g = gm._graph(graphName);
return g._eccentricity(options);
}, false);
aqlfunctions.register('arangodb::GRAPH_ABSOLUTE_CLOSENESS', function (graphName, vertexExample, options) {
var gm = require('@arangodb/general-graph');
var gm = require('@arangodb/general-graph-common');
var g = gm._graph(graphName);
return g._farness(vertexExample, options);
}, false);
aqlfunctions.register('arangodb::GRAPH_CLOSENESS', function (graphName, options) {
var gm = require('@arangodb/general-graph');
var gm = require('@arangodb/general-graph-common');
var g = gm._graph(graphName);
return g._closeness(options);
}, false);
aqlfunctions.register('arangodb::GRAPH_ABSOLUTE_BETWEENNESS', function (graphName, vertexExample, options) {
var gm = require('@arangodb/general-graph');
var gm = require('@arangodb/general-graph-common');
var g = gm._graph(graphName);
return g._absoluteBetweenness(vertexExample, options);
}, false);
aqlfunctions.register('arangodb::GRAPH_BETWEENNESS', function (graphName, options) {
var gm = require('@arangodb/general-graph');
var gm = require('@arangodb/general-graph-common');
var g = gm._graph(graphName);
return g._betweenness(options);
}, false);
aqlfunctions.register('arangodb::GRAPH_RADIUS', function (graphName, options) {
var gm = require('@arangodb/general-graph');
var gm = require('@arangodb/general-graph-common');
var g = gm._graph(graphName);
return g._radius(options);
}, false);
aqlfunctions.register('arangodb::GRAPH_DIAMETER', function (graphName, options) {
var gm = require('@arangodb/general-graph');
var gm = require('@arangodb/general-graph-common');
var g = gm._graph(graphName);
return g._diameter(options);
}, false);

View File

@ -57,6 +57,8 @@ function GeneralGraphCreationSuite() {
var vn3 = "UnitTestVertices3";
var vn4 = "UnitTestVertices4";
var gn = "UnitTestGraph";
var gn1 = "UnitTestGraph1";
var gn2 = "UnitTestGraph2";
var edgeDef = graph._edgeDefinitions(
graph._relation(rn, [vn1], [vn1]),
graph._relation(rn1,
@ -76,6 +78,14 @@ function GeneralGraphCreationSuite() {
vc5 = "UnitTestEdgeDefDeleteVertexCol5",
vc6 = "UnitTestEdgeDefDeleteVertexCol6";
var sortEdgeDefinition = function(edgeDefinition) {
return {
collection: edgeDefinition.collection,
from: edgeDefinition.from.slice().sort(),
to: edgeDefinition.to.slice().sort()
}
};
return {
setUp: function() {
@ -111,6 +121,12 @@ function GeneralGraphCreationSuite() {
graph._drop(gN2, true);
} catch(ignore) {
}
if (db._collection("_graphs").exists(gn1)) {
db._collection("_graphs").remove(gn1);
}
if (db._collection("_graphs").exists(gn2)) {
db._collection("_graphs").remove(gn2);
}
},
////////////////////////////////////////////////////////////////////////////////
@ -381,7 +397,6 @@ function GeneralGraphCreationSuite() {
]);
},
test_create : function () {
if (db._collection("_graphs").exists(gn)) {
db._collection("_graphs").remove(gn);
@ -467,6 +482,64 @@ function GeneralGraphCreationSuite() {
}
},
// Graphs may have overlapping edge collections, as long as their
// definitions are equal.
// This is also a regression test for a broken comparison of edge
// definitions, which did rely on the edge definition object's key order
// and thus sometimes reported spurious inequality.
test_createGraphsWithOverlappingEdgeDefinitions: function () {
if (db._collection("_graphs").exists(gn)) {
db._collection("_graphs").remove(gn);
}
// try to provoke differently ordered keys in
// edge definitions that are actually equal.
const edgeDefs1 = [
{
collection: rn,
from: [vn1],
to: [vn1],
},
];
const edgeDefs2 = [
{
to: [vn1],
from: [vn1],
collection: rn,
},
];
graph._create(gn1, edgeDefs1);
graph._create(gn2, edgeDefs2);
},
// Graphs may have overlapping edge collections, as long as their
// definitions are equal.
// This is also a regression test for a broken comparison of edge
// definitions, which did rely on the edge definition's from and to
// arrays to be ordered.
test_createGraphsWithLargeOverlappingEdgeDefinitions: function () {
if (db._collection("_graphs").exists(gn)) {
db._collection("_graphs").remove(gn);
}
// try to provoke differently ordered from and to arrays in
// edge definitions that are actually equal.
const edgeDefs1 = [
{
collection: rn,
from: [vn1, vn2, vn3, vn4],
to: [vn2, vn1, vn4, vn3],
},
];
const edgeDefs2 = [
{
collection: rn,
from: [vn4, vn3, vn2, vn1],
to: [vn3, vn4, vn1, vn2],
},
];
graph._create(gn1, edgeDefs1);
graph._create(gn2, edgeDefs2);
},
test_get_graph : function () {
if (db._collection("_graphs").exists(gn)) {
db._collection("_graphs").remove(gn);
@ -582,11 +655,17 @@ function GeneralGraphCreationSuite() {
g1._deleteEdgeDefinition(ec1);
assertEqual([dr2, dr3], g1.__edgeDefinitions);
assertEqual([vc1, vc2], g1._orphanCollections());
g1 = graph._graph(gN1); // reload
assertEqual([dr2, dr3], g1.__edgeDefinitions);
assertEqual([vc1, vc2], g1._orphanCollections());
assertTrue(db._collection(ec1) !== null);
g1._deleteEdgeDefinition(ec2);
assertEqual([dr3], g1.__edgeDefinitions);
assertEqual([vc1, vc2, vc3], g1._orphanCollections());
assertEqual([vc1, vc2, vc3].sort(), g1._orphanCollections().sort());
g1 = graph._graph(gN1); // reload
assertEqual([dr3], g1.__edgeDefinitions);
assertEqual([vc1, vc2, vc3].sort(), g1._orphanCollections().sort());
assertTrue(db._collection(ec2) !== null);
},
@ -601,14 +680,20 @@ function GeneralGraphCreationSuite() {
g1._deleteEdgeDefinition(ec1, true);
assertEqual([dr2, dr3], g1.__edgeDefinitions);
assertEqual([vc1, vc2], g1._orphanCollections());
g1 = graph._graph(gN1); // reload
assertEqual([dr2, dr3], g1.__edgeDefinitions);
assertEqual([vc1, vc2], g1._orphanCollections());
assertTrue(db._collection(ec1) === null);
g1._deleteEdgeDefinition(ec2, true);
assertEqual([dr3], g1.__edgeDefinitions);
assertEqual([vc1, vc2, vc3], g1._orphanCollections());
assertEqual([vc1, vc2, vc3].sort(), g1._orphanCollections().sort());
g1 = graph._graph(gN1); // reload
assertEqual([dr3], g1.__edgeDefinitions);
assertEqual([vc1, vc2, vc3].sort(), g1._orphanCollections().sort());
assertTrue(db._collection(ec2) === null);
},
test_extendEdgeDefinitionFromExistingGraph1: function() {
try {
@ -622,10 +707,11 @@ function GeneralGraphCreationSuite() {
try {
g1._extendEdgeDefinitions(dr2);
fail();
} catch (e) {
assertEqual(
e.errorMessage,
arangodb.errors.ERROR_GRAPH_COLLECTION_MULTI_USE.message
ec1 + " " + arangodb.errors.ERROR_GRAPH_COLLECTION_MULTI_USE.message
);
}
@ -680,15 +766,29 @@ function GeneralGraphCreationSuite() {
g1 = graph._create(gN1, [dr1]);
graph._create(gN2, [dr2]);
const loadG1 = () => graph._graph(gN1);
let sortEdgeDefinitions = function (a,b) {
if (a.collection < b.collection) {
return -1;
}
if (a.collection > b.collection) {
return 1;
}
return 0;
};
assertEqual([dr1], g1.__edgeDefinitions);
g1._addVertexCollection(vc3);
assertEqual([vc3], g1._orphanCollections());
assertEqual([vc3], loadG1()._orphanCollections());
g1._extendEdgeDefinitions(dr3);
assertEqual([dr1, dr3], g1.__edgeDefinitions);
assertEqual([dr1, dr3].sort(sortEdgeDefinitions), g1.__edgeDefinitions);
assertEqual([dr1, dr3].sort(sortEdgeDefinitions), loadG1().__edgeDefinitions);
assertEqual([], g1._orphanCollections());
assertEqual([], loadG1()._orphanCollections());
g1._extendEdgeDefinitions(dr2);
assertEqual([dr1, dr3, dr2], g1.__edgeDefinitions);
assertEqual([dr1, dr3, dr2].sort(sortEdgeDefinitions), g1.__edgeDefinitions);
assertEqual([dr1, dr3, dr2].sort(sortEdgeDefinitions), loadG1().__edgeDefinitions);
},
test_extendEdgeDefinitionFromExistingGraph4: function() {
@ -705,15 +805,22 @@ function GeneralGraphCreationSuite() {
dr2 = graph._relation(ec2, [vc4, vc3, vc1, vc2], [vc4, vc3, vc1, vc2]),
g1 = graph._create(gN1, [dr1]);
const loadG1 = () => graph._graph(gN1);
g1._extendEdgeDefinitions(dr2);
dr1 = sortEdgeDefinition(dr1);
dr2 = sortEdgeDefinition(dr2);
assertEqual([dr1, dr2], g1.__edgeDefinitions);
var edgeDefinition = _.find(g1.__edgeDefinitions, {collection: ec2});
assertEqual([dr1, dr2], loadG1().__edgeDefinitions);
let edgeDefinition = _.find(g1.__edgeDefinitions, {collection: ec2});
assertEqual(edgeDefinition.from, [vc1, vc2, vc3, vc4]);
assertEqual(edgeDefinition.to, [vc1, vc2, vc3, vc4]);
edgeDefinition = _.find(loadG1().__edgeDefinitions, {collection: ec2});
assertEqual(edgeDefinition.from, [vc1, vc2, vc3, vc4]);
assertEqual(edgeDefinition.to, [vc1, vc2, vc3, vc4]);
},
test_editEdgeDefinitionFromExistingGraph1: function() {
var dr1 = graph._relation(ec1, [vc1], [vc1, vc2]),
dr2 = graph._relation(ec2, [vc3], [vc4, vc5]),
@ -732,37 +839,42 @@ function GeneralGraphCreationSuite() {
test_editEdgeDefinitionFromExistingGraph2: function() {
var dr1 = graph._relation(ec1, [vc1, vc2], [vc3, vc4]),
dr2 = graph._relation(ec2, [vc1], [vc4]),
dr3 = graph._relation(ec1, [vc5], [vc5]),
g1 = graph._create(gN1, [dr1, dr2]),
g2 = graph._create(gN2, [dr1]);
var dr1 = graph._relation(ec1, _.cloneDeep([vc1, vc2]), _.cloneDeep([vc3, vc4])),
dr2 = graph._relation(ec2, _.cloneDeep([vc1]), _.cloneDeep([vc4])),
dr3 = graph._relation(ec1, _.cloneDeep([vc5]), _.cloneDeep([vc5])),
g1 = graph._create(gN1, _.cloneDeep([dr1, dr2])),
g2 = graph._create(gN2, _.cloneDeep([dr1]));
g1._editEdgeDefinitions(_.cloneDeep(dr3));
g1 = graph._graph(gN1);
g2 = graph._graph(gN2);
g1._editEdgeDefinitions(dr3);
assertEqual([dr3, dr2], g1.__edgeDefinitions);
assertEqual([dr3], g2.__edgeDefinitions);
g2 = graph._graph(gN2);
assertEqual(g1._orphanCollections().sort(), [vc2, vc3].sort());
assertEqual(g2._orphanCollections().sort(), [vc1, vc2, vc3, vc4].sort());
},
test_editEdgeDefinitionFromExistingGraph3: function() {
var dr1 = graph._relation(ec1, [vc1], [vc1, vc2]),
dr2 = graph._relation(ec1, [vc3], [vc4, vc5]),
dr3 = graph._relation(ec2, [vc2], [vc2, vc3]),
g1 = graph._create(gN1, [dr1, dr3]),
g2 = graph._create(gN2, [dr1]);
var dr1 = graph._relation(ec1, _.cloneDeep([vc1]), _.cloneDeep([vc1, vc2])),
dr2 = graph._relation(ec1, _.cloneDeep([vc3]), _.cloneDeep([vc4, vc5])),
dr3 = graph._relation(ec2, _.cloneDeep([vc2]), _.cloneDeep([vc2, vc3])),
g1 = graph._create(gN1, _.cloneDeep([dr1, dr3])),
g2 = graph._create(gN2, _.cloneDeep([dr1]));
g1._addVertexCollection(vc4);
g2._addVertexCollection(vc5);
g2._addVertexCollection(vc6);
g1._editEdgeDefinitions(dr2, true);
g1._editEdgeDefinitions(dr2);
g2 = graph._graph(gN2);
g1 = graph._graph(gN1);
assertEqual([dr2, dr3], g1.__edgeDefinitions);
assertEqual([dr2], g2.__edgeDefinitions);
g2 = graph._graph(gN2);
assertEqual([vc1], g1._orphanCollections());
assertEqual(g2._orphanCollections().sort(), [vc1, vc2, vc6].sort());
@ -919,14 +1031,17 @@ function EdgesAndVerticesSuite() {
var myGraphName = unitTestGraphName + "2";
var myEdgeColName = "unitTestEdgeCollection4711";
var myVertexColName = vc1;
graph._create(
myGraphName,
graph._edgeDefinitions(
graph._relation(myEdgeColName, myVertexColName, myVertexColName)
)
);
graph._drop(myGraphName, true);
assertFalse(graph._exists(myGraphName));
db._flushCache();
assertTrue(db._collection(myVertexColName) !== null);
assertTrue(db._collection(myEdgeColName) === null);
},
@ -1013,11 +1128,9 @@ function EdgesAndVerticesSuite() {
graph._relation(myED, myVD2, myVD2)
)
);
fail();
} catch (e) {
assertEqual(
e.errorMessage,
arangodb.errors.ERROR_GRAPH_COLLECTION_MULTI_USE.message
);
assertEqual(e.errorNum, arangodb.errors.ERROR_GRAPH_COLLECTION_MULTI_USE.code)
}
assertFalse(graph._exists(myGraphName));
assertTrue(db._collection(myVD1) === null);
@ -1144,14 +1257,14 @@ function EdgesAndVerticesSuite() {
g[ec1].save(vertexId1, vertexId2, {});
fail();
} catch (e) {
assertEqual(e.errorNum, 1906);
assertEqual(e.errorNum, arangodb.errors.ERROR_GRAPH_INVALID_EDGE.code);
}
try {
g[ec1].insert(vertexId1, vertexId2, {});
fail();
} catch (e) {
assertEqual(e.errorNum, 1906);
assertEqual(e.errorNum, arangodb.errors.ERROR_GRAPH_INVALID_EDGE.code);
}
g[vc1].remove(vertexId1);
@ -1764,9 +1877,10 @@ function OrphanCollectionSuite() {
g1._addVertexCollection(vC4, false);
} catch (e) {
assertEqual(e.errorNum, ERRORS.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.code);
assertEqual(e.errorMessage, vC4 + ERRORS.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.message);
assertEqual(e.errorMessage, vC4 + " " + ERRORS.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.message);
}
assertTrue(db._collection(vC4) === null);
g1 = graph._graph(gN1);
assertEqual(g1._orphanCollections(), []);
},
@ -1786,7 +1900,7 @@ function OrphanCollectionSuite() {
g1._addVertexCollection(vC1);
} catch (e) {
assertEqual(e.errorNum, ERRORS.ERROR_GRAPH_COLLECTION_USED_IN_EDGE_DEF.code);
assertEqual(e.errorMessage, ERRORS.ERROR_GRAPH_COLLECTION_USED_IN_EDGE_DEF.message);
assertEqual(e.errorMessage, vC1 + " " + ERRORS.ERROR_GRAPH_COLLECTION_USED_IN_EDGE_DEF.message);
}
},
@ -1795,18 +1909,19 @@ function OrphanCollectionSuite() {
try {
g1._removeVertexCollection(name);
} catch (e) {
assertEqual(e.errorNum, ERRORS.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.code);
assertEqual(e.errorMessage, ERRORS.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.message);
assertEqual(e.errorNum, ERRORS.ERROR_GRAPH_NOT_IN_ORPHAN_COLLECTION.code);
assertEqual(e.errorMessage, ERRORS.ERROR_GRAPH_NOT_IN_ORPHAN_COLLECTION.message);
}
},
test_removeVertexCollection2: function() {
g1._addVertexCollection(vC4, true);
g1._addVertexCollection(vC5, true);
assertEqual(g1._orphanCollections(), [vC4, vC5]);
assertEqual(g1._orphanCollections().sort(), [vC4, vC5].sort());
g1._removeVertexCollection(vC4, false);
assertTrue(db._collection(vC4) !== null);
assertEqual(g1._orphanCollections(), [vC5]);
g1 = graph._graph(gN1);
assertEqual(g1._orphanCollections().sort(), [vC5].sort());
try {
g1._removeVertexCollection(vC4, true);
} catch (e) {
@ -1822,6 +1937,7 @@ function OrphanCollectionSuite() {
graph._drop(gN1, true);
assertTrue(db._collection(vC3) !== null);
graph._drop(gN2, true);
db._flushCache();
assertTrue(db._collection(vC3) === null);
},
@ -1832,6 +1948,7 @@ function OrphanCollectionSuite() {
graph._drop(gN2, true);
assertTrue(db._collection(vC3) !== null);
graph._drop(gN1, true);
db._flushCache();
assertTrue(db._collection(vC3) === null);
},
@ -1843,6 +1960,7 @@ function OrphanCollectionSuite() {
graph._drop(gN1, true);
assertTrue(db._collection(vC4) !== null);
graph._drop(gN2, true);
db._flushCache();
assertTrue(db._collection(vC4) === null);
},

View File

@ -62,6 +62,27 @@
exports.ArangoUsers = global.ArangoUsers;
delete global.ArangoUsers;
// //////////////////////////////////////////////////////////////////////////////
// / @brief ArangoGeneralGraphModule
// //////////////////////////////////////////////////////////////////////////////
exports.ArangoGeneralGraphModule = global.ArangoGeneralGraphModule;
delete global.ArangoGeneralGraphModule;
// //////////////////////////////////////////////////////////////////////////////
// / @brief ArangoGeneralGraphClass
// //////////////////////////////////////////////////////////////////////////////
exports.ArangoGraph = global.ArangoGraph;
delete global.ArangoGraph;
// //////////////////////////////////////////////////////////////////////////////
// / @brief ArangoSmartGraphClass
// //////////////////////////////////////////////////////////////////////////////
exports.ArangoSmartGraph = global.ArangoSmartGraph;
delete global.ArangoSmartGraph;
// //////////////////////////////////////////////////////////////////////////////
// / @brief ArangoDatabase

View File

@ -47,8 +47,7 @@ const isZipBuffer = require('@arangodb/util').isZipBuffer;
const SYSTEM_SERVICE_MOUNTS = [
'/_admin/aardvark', // Admin interface.
'/_api/foxx', // Foxx management API.
'/_api/gharial' // General_Graph API.
'/_api/foxx' // Foxx management API.
];
const GLOBAL_SERVICE_MAP = new Map();

View File

@ -0,0 +1,100 @@
/* global ArangoGeneralGraph */
'use strict';
// //////////////////////////////////////////////////////////////////////////////
// / @brief Replication management
// /
// / @file
// /
// / DISCLAIMER
// /
// / Copyright 2012 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 Heiko Kernbach
// / @author Copyright 2018, ArangoDB GmbH, Cologne, Germany
// //////////////////////////////////////////////////////////////////////////////
const internal = require('internal'); // OK: reloadAuth
const ggc = require('@arangodb/general-graph-common');
const GeneralGraph = internal.ArangoGeneralGraphModule;
// This is supposed to be a class
const ArangoGraph = internal.ArangoGraph;
//const arangodb = require("@arangodb");
// inherited graph class
let CommonGraph = ggc.__GraphClass;
// new c++ based
CommonGraph.prototype.__updateDefinitions = function (edgeDefs, orphans) {
this.__edgeDefinitions = edgeDefs;
this.__orphanCollections = orphans;
};
CommonGraph.prototype._deleteEdgeDefinition = function (edgeDefinition, dropCollection = false) {
let result = ArangoGraph._deleteEdgeDefinition(this.__name, edgeDefinition, dropCollection);
this.__updateDefinitions(result.graph.edgeDefinitions, result.graph.orphanCollections);
};
CommonGraph.prototype._extendEdgeDefinitions = function (edgeDefinitions) {
let result = ArangoGraph._extendEdgeDefinitions(this.__name, edgeDefinitions);
this.__updateDefinitions(result.graph.edgeDefinitions, result.graph.orphanCollections);
};
CommonGraph.prototype._editEdgeDefinitions = function (edgeDefinitions) {
let result = ArangoGraph._editEdgeDefinitions(this.__name, edgeDefinitions);
this.__updateDefinitions(result.graph.edgeDefinitions, result.graph.orphanCollections);
};
CommonGraph.prototype._addVertexCollection = function (vertexName, createCollection = true) {
let result = ArangoGraph._addVertexCollection(this.__name, vertexName, createCollection);
this.__updateDefinitions(result.graph.edgeDefinitions, result.graph.orphanCollections);
};
CommonGraph.prototype._removeVertexCollection = function (vertexName, dropCollection = false) {
let result = ArangoGraph._removeVertexCollection(this.__name, vertexName, dropCollection);
this.__updateDefinitions(result.graph.edgeDefinitions, result.graph.orphanCollections);
};
exports._create = function (name, edgeDefinition, orphans, options) {
let g = GeneralGraph._create(name, edgeDefinition, orphans, options);
return new CommonGraph(g.graph);
};
exports._drop = GeneralGraph._drop;
exports._exists = GeneralGraph._exists;
exports._graph = function (graphName) {
let g = GeneralGraph._graph(graphName);
return new CommonGraph(g);
};
exports._list = GeneralGraph._list;
exports._listObjects = GeneralGraph._listObjects;
exports._renameCollection = GeneralGraph._renameCollection;
// js based helper functions
exports.__GraphClass = CommonGraph;
exports._edgeDefinitions = ggc._edgeDefinitions;
exports._extendEdgeDefinitions = ggc._extendEdgeDefinitions;
exports._relation = ggc._relation;
exports._registerCompatibilityFunctions = ggc._registerCompatibilityFunctions;

View File

@ -30,6 +30,8 @@ class Result {
public:
Result() : _errorNumber(TRI_ERROR_NO_ERROR) {}
Result(bool avoidCastingErrors) = delete;
Result(int errorNumber)
: _errorNumber(errorNumber){
if (errorNumber != TRI_ERROR_NO_ERROR) {

View File

@ -64,12 +64,18 @@ std::string const StaticStrings::ReplaceExisting("replaceExisting");
std::string const StaticStrings::OverWrite("overwrite");
// replication headers
std::string const StaticStrings::ReplicationHeaderCheckMore("x-arango-replication-checkmore");
std::string const StaticStrings::ReplicationHeaderLastIncluded("x-arango-replication-lastincluded");
std::string const StaticStrings::ReplicationHeaderLastScanned("x-arango-replication-lastscanned");
std::string const StaticStrings::ReplicationHeaderLastTick("x-arango-replication-lasttick");
std::string const StaticStrings::ReplicationHeaderFromPresent("x-arango-replication-frompresent");
std::string const StaticStrings::ReplicationHeaderActive("x-arango-replication-active");
std::string const StaticStrings::ReplicationHeaderCheckMore(
"x-arango-replication-checkmore");
std::string const StaticStrings::ReplicationHeaderLastIncluded(
"x-arango-replication-lastincluded");
std::string const StaticStrings::ReplicationHeaderLastScanned(
"x-arango-replication-lastscanned");
std::string const StaticStrings::ReplicationHeaderLastTick(
"x-arango-replication-lasttick");
std::string const StaticStrings::ReplicationHeaderFromPresent(
"x-arango-replication-frompresent");
std::string const StaticStrings::ReplicationHeaderActive(
"x-arango-replication-active");
// database and collection names
std::string const StaticStrings::SystemDatabase("_system");
@ -155,5 +161,30 @@ std::string const StaticStrings::MimeTypeText("text/plain; charset=utf-8");
std::string const StaticStrings::MimeTypeVPack("application/x-velocypack");
std::string const StaticStrings::MultiPartContentType("multipart/form-data");
// collection attributes
std::string const StaticStrings::NumberOfShards("numberOfShards");
std::string const StaticStrings::ReplicationFactor("replicationFactor");
std::string const StaticStrings::DistributeShardsLike("distributeShardsLike");
// graph attribute names
std::string const StaticStrings::GraphCollection("_graphs");
std::string const StaticStrings::GraphIsSmart("isSmart");
std::string const StaticStrings::GraphFrom("from");
std::string const StaticStrings::GraphTo("to");
std::string const StaticStrings::GraphOptions("options");
std::string const StaticStrings::GraphSmartGraphAttribute(
"smartGraphAttribute");
std::string const StaticStrings::GraphEdgeDefinitions("edgeDefinitions");
std::string const StaticStrings::GraphOrphans("orphanCollections");
std::string const StaticStrings::GraphInitial("initial");
std::string const StaticStrings::GraphInitialCid("initialCid");
std::string const StaticStrings::GraphName("name");
// rest query parameter
std::string const StaticStrings::GraphDropCollections("dropCollections");
std::string const StaticStrings::GraphDropCollection("dropCollection");
std::string const StaticStrings::GraphCreateCollections("createCollections");
std::string const StaticStrings::GraphCreateCollection("createCollection");
// misc strings
std::string const StaticStrings::LastValue("lastValue");

View File

@ -148,6 +148,26 @@ class StaticStrings {
static std::string const MimeTypeVPack;
static std::string const MultiPartContentType;
// graph attribute names
static std::string const GraphCollection;
static std::string const GraphIsSmart;
static std::string const GraphFrom;
static std::string const GraphTo;
static std::string const GraphOptions;
static std::string const GraphSmartGraphAttribute;
static std::string const NumberOfShards;
static std::string const DistributeShardsLike;
static std::string const ReplicationFactor;
static std::string const GraphDropCollections;
static std::string const GraphDropCollection;
static std::string const GraphCreateCollections;
static std::string const GraphCreateCollection;
static std::string const GraphEdgeDefinitions;
static std::string const GraphOrphans;
static std::string const GraphInitial;
static std::string const GraphInitialCid;
static std::string const GraphName;
// misc strings
static std::string const LastValue;
};

View File

@ -376,6 +376,10 @@ ERROR_GRAPH_INVALID_ID,1937,"Invalid id","Invalid id",
ERROR_GRAPH_COLLECTION_USED_IN_ORPHANS,1938,"collection used in orphans","The collection is already used in the orphans of the graph.",
ERROR_GRAPH_EDGE_COL_DOES_NOT_EXIST,1939,"edge collection does not exist or is not part of the graph","the specified edge collection does not exist or is not part of the graph.",
ERROR_GRAPH_EMPTY,1940,"empty graph","The requested graph has no edge collections."
ERROR_GRAPH_INTERNAL_DATA_CORRUPT,1941,"internal graph data corrupt","The _graphs collection contains invalid data."
ERROR_GRAPH_INTERNAL_EDGE_COLLECTION_ALREADY_SET,1942,"edge collection already set","Tried to add an edge collection which is already defined."
ERROR_GRAPH_CREATE_MALFORMED_ORPHAN_LIST,1943,"malformed orphan list","the orphan list argument is malformed. It has to be an array of strings."
ERROR_GRAPH_EDGE_DEFINITION_IS_DOCUMENT,1944,"edge definition collection is a document collection","the collection used as a relation is existing, but is a document collection, it cannot be used here."
################################################################################
## Session errors
@ -436,6 +440,7 @@ ERROR_NO_SMART_GRAPH_ATTRIBUTE,4001,"smart graph attribute not given","The given
ERROR_CANNOT_DROP_SMART_COLLECTION,4002,"cannot drop this smart collection","This smart collection cannot be dropped, it dictates sharding in the graph."
ERROR_KEY_MUST_BE_PREFIXED_WITH_SMART_GRAPH_ATTRIBUTE,4003,"in smart vertex collections _key must be prefixed with the value of the smart graph attribute","In a smart vertex collection _key must be prefixed with the value of the smart graph attribute."
ERROR_ILLEGAL_SMART_GRAPH_ATTRIBUTE,4004,"attribute cannot be used as smart graph attribute","The given smartGraph attribute is illegal and connot be used for sharding. All system attributes are forbidden."
ERROR_SMART_GRAPH_ATTRIBUTE_MISMATCH,4005,"smart graph attribute mismatch","The smart graph attribute of the given collection does not match the smart graph attribute of the graph."
################################################################################
## Cluster repair errors

View File

@ -285,6 +285,10 @@ void TRI_InitializeErrorMessages() {
REG_ERROR(ERROR_GRAPH_COLLECTION_USED_IN_ORPHANS, "collection used in orphans");
REG_ERROR(ERROR_GRAPH_EDGE_COL_DOES_NOT_EXIST, "edge collection does not exist or is not part of the graph");
REG_ERROR(ERROR_GRAPH_EMPTY, "empty graph");
REG_ERROR(ERROR_GRAPH_INTERNAL_DATA_CORRUPT, "internal graph data corrupt");
REG_ERROR(ERROR_GRAPH_INTERNAL_EDGE_COLLECTION_ALREADY_SET, "edge collection already set");
REG_ERROR(ERROR_GRAPH_CREATE_MALFORMED_ORPHAN_LIST, "malformed orphan list");
REG_ERROR(ERROR_GRAPH_EDGE_DEFINITION_IS_DOCUMENT, "edge definition collection is a document collection");
REG_ERROR(ERROR_SESSION_UNKNOWN, "unknown session");
REG_ERROR(ERROR_SESSION_EXPIRED, "session expired");
REG_ERROR(SIMPLE_CLIENT_UNKNOWN_ERROR, "unknown client error");
@ -315,6 +319,7 @@ void TRI_InitializeErrorMessages() {
REG_ERROR(ERROR_CANNOT_DROP_SMART_COLLECTION, "cannot drop this smart collection");
REG_ERROR(ERROR_KEY_MUST_BE_PREFIXED_WITH_SMART_GRAPH_ATTRIBUTE, "in smart vertex collections _key must be prefixed with the value of the smart graph attribute");
REG_ERROR(ERROR_ILLEGAL_SMART_GRAPH_ATTRIBUTE, "attribute cannot be used as smart graph attribute");
REG_ERROR(ERROR_SMART_GRAPH_ATTRIBUTE_MISMATCH, "smart graph attribute mismatch");
REG_ERROR(ERROR_CLUSTER_REPAIRS_FAILED, "error during cluster repairs");
REG_ERROR(ERROR_CLUSTER_REPAIRS_NOT_ENOUGH_HEALTHY, "not enough (healthy) db servers");
REG_ERROR(ERROR_CLUSTER_REPAIRS_REPLICATION_FACTOR_VIOLATED, "replication factor violated during cluster repairs");

View File

@ -1512,6 +1512,27 @@ constexpr int TRI_ERROR_GRAPH_EDGE_COL_DOES_NOT_EXIST
/// The requested graph has no edge collections.
constexpr int TRI_ERROR_GRAPH_EMPTY = 1940;
/// 1941: ERROR_GRAPH_INTERNAL_DATA_CORRUPT
/// "internal graph data corrupt"
/// The _graphs collection contains invalid data.
constexpr int TRI_ERROR_GRAPH_INTERNAL_DATA_CORRUPT = 1941;
/// 1942: ERROR_GRAPH_INTERNAL_EDGE_COLLECTION_ALREADY_SET
/// "edge collection already set"
/// Tried to add an edge collection which is already defined.
constexpr int TRI_ERROR_GRAPH_INTERNAL_EDGE_COLLECTION_ALREADY_SET = 1942;
/// 1943: ERROR_GRAPH_CREATE_MALFORMED_ORPHAN_LIST
/// "malformed orphan list"
/// the orphan list argument is malformed. It has to be an array of strings.
constexpr int TRI_ERROR_GRAPH_CREATE_MALFORMED_ORPHAN_LIST = 1943;
/// 1944: ERROR_GRAPH_EDGE_DEFINITION_IS_DOCUMENT
/// "edge definition collection is a document collection"
/// the collection used as a relation is existing, but is a document
/// collection, it cannot be used here.
constexpr int TRI_ERROR_GRAPH_EDGE_DEFINITION_IS_DOCUMENT = 1944;
/// 1950: ERROR_SESSION_UNKNOWN
/// "unknown session"
/// Will be raised when an invalid/unknown session id is passed to the server.
@ -1665,6 +1686,12 @@ constexpr int TRI_ERROR_KEY_MUST_BE_PREFIXED_WITH_SMART_GRAPH_ATTRIBUTE
/// All system attributes are forbidden.
constexpr int TRI_ERROR_ILLEGAL_SMART_GRAPH_ATTRIBUTE = 4004;
/// 4005: ERROR_SMART_GRAPH_ATTRIBUTE_MISMATCH
/// "smart graph attribute mismatch"
/// The smart graph attribute of the given collection does not match the smart
/// graph attribute of the graph.
constexpr int TRI_ERROR_SMART_GRAPH_ATTRIBUTE_MISMATCH = 4005;
/// 5000: ERROR_CLUSTER_REPAIRS_FAILED
/// "error during cluster repairs"
/// General error during cluster repairs

View File

@ -49,6 +49,39 @@ enum class RequestType {
ILLEGAL // must be last
};
inline const char* requestToString(RequestType requestType) {
switch (requestType) {
case RequestType::DELETE_REQ:
return "DELETE";
case RequestType::GET:
return "GET";
case RequestType::POST:
return "POST";
case RequestType::PUT:
return "PUT";
case RequestType::HEAD:
return "HEAD";
case RequestType::PATCH:
return "PATCH";
case RequestType::OPTIONS:
return "OPTIONS";
case RequestType::VSTREAM_CRED:
return "VSTREAM_CRED";
case RequestType::VSTREAM_REGISTER:
return "VSTREAM_REGISTER";
case RequestType::VSTREAM_STATUS:
return "VSTREAM_STATUS";
case RequestType::ILLEGAL:
default:
return "ILLEGAL";
}
}
inline std::ostream& operator<<(std::ostream& ostream,
RequestType requestType) {
return ostream << requestToString(requestType);
}
enum class ContentType {
CUSTOM, // use Content-Type from _headers
JSON, // application/json
@ -122,6 +155,112 @@ enum class ResponseCode {
BANDWIDTH_LIMIT_EXCEEDED = 509,
NOT_EXTENDED = 510
};
inline const char* responseToString(ResponseCode responseCode) {
switch (responseCode) {
case ResponseCode::CONTINUE:
return "100 CONTINUE";
case ResponseCode::SWITCHING_PROTOCOLS:
return "101 SWITCHING_PROTOCOLS";
case ResponseCode::PROCESSING:
return "102 PROCESSING";
case ResponseCode::OK:
return "200 OK";
case ResponseCode::CREATED:
return "201 CREATED";
case ResponseCode::ACCEPTED:
return "202 ACCEPTED";
case ResponseCode::PARTIAL:
return "203 PARTIAL";
case ResponseCode::NO_CONTENT:
return "204 NO_CONTENT";
case ResponseCode::RESET_CONTENT:
return "205 RESET_CONTENT";
case ResponseCode::PARTIAL_CONTENT:
return "206 PARTIAL_CONTENT";
case ResponseCode::MOVED_PERMANENTLY:
return "301 MOVED_PERMANENTLY";
case ResponseCode::FOUND:
return "302 FOUND";
case ResponseCode::SEE_OTHER:
return "303 SEE_OTHER";
case ResponseCode::NOT_MODIFIED:
return "304 NOT_MODIFIED";
case ResponseCode::TEMPORARY_REDIRECT:
return "307 TEMPORARY_REDIRECT";
case ResponseCode::PERMANENT_REDIRECT:
return "308 PERMANENT_REDIRECT";
case ResponseCode::BAD:
return "400 BAD";
case ResponseCode::UNAUTHORIZED:
return "401 UNAUTHORIZED";
case ResponseCode::PAYMENT_REQUIRED:
return "402 PAYMENT_REQUIRED";
case ResponseCode::FORBIDDEN:
return "403 FORBIDDEN";
case ResponseCode::NOT_FOUND:
return "404 NOT_FOUND";
case ResponseCode::METHOD_NOT_ALLOWED:
return "405 METHOD_NOT_ALLOWED";
case ResponseCode::NOT_ACCEPTABLE:
return "406 NOT_ACCEPTABLE";
case ResponseCode::REQUEST_TIMEOUT:
return "408 REQUEST_TIMEOUT";
case ResponseCode::CONFLICT:
return "409 CONFLICT";
case ResponseCode::GONE:
return "410 GONE";
case ResponseCode::LENGTH_REQUIRED:
return "411 LENGTH_REQUIRED";
case ResponseCode::PRECONDITION_FAILED:
return "412 PRECONDITION_FAILED";
case ResponseCode::REQUEST_ENTITY_TOO_LARGE:
return "413 REQUEST_ENTITY_TOO_LARGE";
case ResponseCode::REQUEST_URI_TOO_LONG:
return "414 REQUEST_URI_TOO_LONG";
case ResponseCode::UNSUPPORTED_MEDIA_TYPE:
return "415 UNSUPPORTED_MEDIA_TYPE";
case ResponseCode::REQUESTED_RANGE_NOT_SATISFIABLE:
return "416 REQUESTED_RANGE_NOT_SATISFIABLE";
case ResponseCode::EXPECTATION_FAILED:
return "417 EXPECTATION_FAILED";
case ResponseCode::I_AM_A_TEAPOT:
return "418 I_AM_A_TEAPOT";
case ResponseCode::UNPROCESSABLE_ENTITY:
return "422 UNPROCESSABLE_ENTITY";
case ResponseCode::LOCKED:
return "423 LOCKED";
case ResponseCode::PRECONDITION_REQUIRED:
return "428 PRECONDITION_REQUIRED";
case ResponseCode::TOO_MANY_REQUESTS:
return "429 TOO_MANY_REQUESTS";
case ResponseCode::REQUEST_HEADER_FIELDS_TOO_LARGE:
return "431 REQUEST_HEADER_FIELDS_TOO_LARGE";
case ResponseCode::UNAVAILABLE_FOR_LEGAL_REASONS:
return "451 UNAVAILABLE_FOR_LEGAL_REASONS";
case ResponseCode::SERVER_ERROR:
return "500 SERVER_ERROR";
case ResponseCode::NOT_IMPLEMENTED:
return "501 NOT_IMPLEMENTED";
case ResponseCode::BAD_GATEWAY:
return "502 BAD_GATEWAY";
case ResponseCode::SERVICE_UNAVAILABLE:
return "503 SERVICE_UNAVAILABLE";
case ResponseCode::HTTP_VERSION_NOT_SUPPORTED:
return "505 HTTP_VERSION_NOT_SUPPORTED";
case ResponseCode::BANDWIDTH_LIMIT_EXCEEDED:
return "509 BANDWIDTH_LIMIT_EXCEEDED";
case ResponseCode::NOT_EXTENDED:
return "510 NOT_EXTENDED";
default:
return "??? UNEXPECTED";
}
}
inline std::ostream& operator<<(std::ostream& ostream,
ResponseCode responseCode) {
return ostream << responseToString(responseCode);
}
}
}
#endif

View File

@ -347,7 +347,6 @@ rest::ResponseCode GeneralResponse::responseCode(int code) {
case TRI_ERROR_GRAPH_WRONG_COLLECTION_TYPE_VERTEX:
case TRI_ERROR_GRAPH_NOT_IN_ORPHAN_COLLECTION:
case TRI_ERROR_GRAPH_COLLECTION_USED_IN_EDGE_DEF:
case TRI_ERROR_GRAPH_EDGE_COLLECTION_NOT_USED:
case TRI_ERROR_GRAPH_INVALID_EXAMPLE_ARRAY_OBJECT_STRING:
case TRI_ERROR_GRAPH_INVALID_EXAMPLE_ARRAY_OBJECT:
case TRI_ERROR_GRAPH_INVALID_NUMBER_OF_ARGUMENTS:
@ -380,6 +379,7 @@ rest::ResponseCode GeneralResponse::responseCode(int code) {
case TRI_ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST:
case TRI_ERROR_GRAPH_NO_GRAPH_COLLECTION:
case TRI_ERROR_QUEUE_UNKNOWN:
case TRI_ERROR_GRAPH_EDGE_COLLECTION_NOT_USED:
return ResponseCode::NOT_FOUND;
case TRI_ERROR_CLUSTER_SHARD_LEADER_REFUSES_REPLICATION:

View File

@ -38,6 +38,11 @@ TRI_v8_global_t::TRI_v8_global_t(v8::Isolate* isolate)
VocbaseTempl(),
EnvTempl(),
UsersTempl(),
GeneralGraphModuleTempl(),
GeneralGraphTempl(),
#ifdef USE_ENTERPRISE
SmartGraphTempl(),
#endif
BufferTempl(),

View File

@ -363,6 +363,19 @@ struct TRI_v8_global_t {
/// @brief users template
v8::Persistent<v8::ObjectTemplate> UsersTempl;
/// @brief general graphs module template
v8::Persistent<v8::ObjectTemplate> GeneralGraphModuleTempl;
/// @brief general graph class template
v8::Persistent<v8::ObjectTemplate> GeneralGraphTempl;
#ifdef USE_ENTERPRISE
/// @brief smart graph class template
v8::Persistent<v8::ObjectTemplate> SmartGraphTempl;
// there is no smart graph module becuase they are
// identical, just return different graph instances.
#endif
/// @brief Buffer template
v8::Persistent<v8::FunctionTemplate> BufferTempl;

File diff suppressed because it is too large Load Diff