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 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 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. 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} @RESTQUERYPARAM{waitForSync,boolean,optional}
Define if the request should wait until synced to disk. 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 @RESTHEADERPARAMETERS
@RESTHEADERPARAM{if-match,string,optional} @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} @RESTQUERYPARAM{keepNull,boolean,optional}
Define if values set to null should be stored. By default the key is not removed from the document. 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} @RESTALLBODYPARAM{updateAttributes,object,required}
The body has to be a JSON object containing the attributes to be updated. 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} @RESTQUERYPARAM{waitForSync,boolean,optional}
Define if the request should wait until synced to disk. 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 @RESTHEADERPARAMETERS
@RESTHEADERPARAM{if-match,string,optional} @RESTHEADERPARAM{if-match,string,optional}

View File

@ -23,6 +23,14 @@ The *_key* attribute of the vertex.
@RESTQUERYPARAM{waitForSync,boolean,optional} @RESTQUERYPARAM{waitForSync,boolean,optional}
Define if the request should wait until synced to disk. 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 @RESTHEADERPARAMETERS
@RESTHEADERPARAM{if-match,string,optional} @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} @RESTQUERYPARAM{keepNull,boolean,optional}
Define if values set to null should be stored. By default the key is not removed from the document. 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 @RESTHEADERPARAMETERS
@RESTHEADERPARAM{if-match,string,optional} @RESTHEADERPARAM{if-match,string,optional}

View File

@ -23,6 +23,16 @@ The *_key* attribute of the vertex.
@RESTQUERYPARAM{waitForSync,boolean,optional} @RESTQUERYPARAM{waitForSync,boolean,optional}
Define if the request should wait until synced to disk. 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 @RESTHEADERPARAMETERS
@RESTHEADERPARAM{if-match,string,optional} @RESTHEADERPARAM{if-match,string,optional}

View File

@ -159,7 +159,6 @@ describe ArangoDB do
return doc return doc
end end
def create_edge (waitForSync, graph_name, collection, from, to, body, options = {}) def create_edge (waitForSync, graph_name, collection, from, to, body, options = {})
cmd = edge_endpoint(graph_name, collection) cmd = edge_endpoint(graph_name, collection)
cmd = cmd + "?waitForSync=#{waitForSync}" cmd = cmd + "?waitForSync=#{waitForSync}"
@ -282,6 +281,7 @@ describe ArangoDB do
second_def = { "collection" => bought_collection, "from" => [user_collection], "to" => [product_collection] } second_def = { "collection" => bought_collection, "from" => [user_collection], "to" => [product_collection] }
doc = additional_edge_definition(sync, graph_name, second_def ) doc = additional_edge_definition(sync, graph_name, second_def )
edge_definition.push(second_def) edge_definition.push(second_def)
edge_definition = edge_definition.sort_by { |d| [ -d["collection"] ] }
doc.code.should eq(202) doc.code.should eq(202)
doc.parsed_response['error'].should eq(false) doc.parsed_response['error'].should eq(false)
@ -848,7 +848,10 @@ describe ArangoDB do
it "can not replace a non existing edge" do it "can not replace a non existing edge" do
key = "unknownKey" 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.code.should eq(404)
doc.parsed_response['error'].should eq(true) doc.parsed_response['error'].should eq(true)
doc.parsed_response['errorMessage'].should include("document not found") doc.parsed_response['errorMessage'].should include("document not found")
@ -1169,11 +1172,18 @@ describe ArangoDB do
doc.parsed_response['code'].should eq(404) doc.parsed_response['code'].should eq(404)
end 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) def check404Edge (doc)
check404(doc) check404(doc)
doc.parsed_response['errorNum'].should eq(1930) doc.parsed_response['errorNum'].should eq(1930)
doc.parsed_response['errorMessage'].should eq("edge collection not used in graph") doc.parsed_response['errorMessage'].should eq("edge collection not used in graph")
end end
def check404Vertex (doc) def check404Vertex (doc)
@ -1181,10 +1191,23 @@ describe ArangoDB do
doc.parsed_response['errorNum'].should eq(1926) doc.parsed_response['errorNum'].should eq(1926)
end 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) def check404CRUD (doc)
check404(doc) check404(doc)
doc.parsed_response['errorNum'].should eq(1203) 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 end
it "change edge definition" do it "change edge definition" do
@ -1197,7 +1220,8 @@ describe ArangoDB do
end end
it "delete vertex collection" do 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 end
it "create vertex" do it "create vertex" do
@ -1208,6 +1232,8 @@ describe ArangoDB do
check404CRUD(get_vertex(graph_name, unknown_name, unknown_name)) check404CRUD(get_vertex(graph_name, unknown_name, unknown_name))
end end
# TODO add tests where the edge/vertex collection is not part of the graph, but
# the given key exists!
it "update vertex" do it "update vertex" do
check404CRUD(update_vertex( sync, graph_name, unknown_name, unknown_name, {})) check404CRUD(update_vertex( sync, graph_name, unknown_name, unknown_name, {}))
end end
@ -1221,7 +1247,7 @@ describe ArangoDB do
end end
it "create edge" do 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 end
it "get edge" do it "get edge" do
@ -1277,7 +1303,10 @@ describe ArangoDB do
end end
it "replace edge" do 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 end
it "delete edge" do it "delete edge" do

View File

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

View File

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

View File

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

View File

@ -21,20 +21,17 @@
/// @author Michael Hackstein /// @author Michael Hackstein
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <velocypack/Iterator.h>
#include "Graphs.h" #include "Graphs.h"
#include "Aql/AstNode.h" #include "Aql/AstNode.h"
#include "Basics/StaticStrings.h" #include "Basics/StaticStrings.h"
#include "Basics/VelocyPackHelper.h" #include "Basics/VelocyPackHelper.h"
#include "Graph/Graph.h"
#include <velocypack/Iterator.h>
#include <velocypack/velocypack-aliases.h>
using namespace arangodb::basics; using namespace arangodb::basics;
using namespace arangodb::aql; using namespace arangodb::aql;
char const* Graph::_attrEdgeDefs = "edgeDefinitions";
char const* Graph::_attrOrphans = "orphanCollections";
EdgeConditionBuilder::EdgeConditionBuilder(AstNode* modCondition) EdgeConditionBuilder::EdgeConditionBuilder(AstNode* modCondition)
: _fromCondition(nullptr), : _fromCondition(nullptr),
_toCondition(nullptr), _toCondition(nullptr),
@ -161,83 +158,3 @@ void EdgeConditionBuilderContainer::setVertexId(std::string const& id) {
_compareNode->setStringValue(id.c_str(), id.length()); _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 #ifndef ARANGOD_AQL_GRAPHS_H
#define ARANGOD_AQL_GRAPHS_H 1 #define ARANGOD_AQL_GRAPHS_H 1
#include "Basics/Common.h"
#include "Aql/VariableGenerator.h" #include "Aql/VariableGenerator.h"
#include "Basics/Common.h"
namespace arangodb { namespace arangodb {
@ -141,51 +141,6 @@ class EdgeConditionBuilderContainer final : public EdgeConditionBuilder {
VariableGenerator _varGen; 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 aql
} // namespace arangodb } // namespace arangodb

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -34,7 +34,7 @@ namespace arangodb {
class GeneralRequest; class GeneralRequest;
class RequestStatistics; class RequestStatistics;
enum class RestStatus { DONE, WAITING, FAIL}; enum class RestStatus { DONE, WAITING, FAIL };
namespace rest { namespace rest {
class RestHandler : public std::enable_shared_from_this<RestHandler> { 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 "RestControlPregelHandler.h"
#include "ApplicationFeatures/ApplicationServer.h" #include "ApplicationFeatures/ApplicationServer.h"
#include "Aql/Graphs.h"
#include "Basics/VelocyPackHelper.h" #include "Basics/VelocyPackHelper.h"
#include "Cluster/ServerState.h" #include "Cluster/ServerState.h"
#include "Pregel/Conductor.h" #include "Pregel/Conductor.h"
@ -33,7 +32,8 @@
#include "Transaction/StandaloneContext.h" #include "Transaction/StandaloneContext.h"
#include "V8/v8-vpack.h" #include "V8/v8-vpack.h"
#include "V8Server/V8DealerFeature.h" #include "V8Server/V8DealerFeature.h"
#include "VocBase/Graphs.h" #include "Graph/Graph.h"
#include "Graph/GraphManager.h"
#include "VocBase/Methods/Tasks.h" #include "VocBase/Methods/Tasks.h"
#include <velocypack/Builder.h> #include <velocypack/Builder.h>
@ -133,12 +133,13 @@ void RestControlPregelHandler::startExecution() {
return; return;
} }
auto ctx = transaction::StandaloneContext::Create(_vocbase); graph::GraphManager gmngr{_vocbase};
auto graph = lookupGraphByName(ctx, gs); auto graphRes = gmngr.lookupGraphByName(gs);
if (nullptr == graph) { if (graphRes.fail()) {
generateError(TRI_ERROR_GRAPH_NOT_FOUND); generateError(graphRes.copy_result());
return; return;
} }
std::unique_ptr<graph::Graph> graph = std::move(graphRes.get());
auto gv = graph->vertexCollections(); auto gv = graph->vertexCollections();
for (auto& v : gv) { 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()) { if (ClusterInfo* clusterInfo = ClusterInfo::instance()) {
clusterInfo->loadPlan(); clusterInfo->loadPlan();
} }
} }
return RestStatus::DONE; 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"; std::string const RestVocbaseBaseHandler::EDGES_PATH = "/_api/edges";
////////////////////////////////////////////////////////////////////////////////
/// @brief gharial graph api path
////////////////////////////////////////////////////////////////////////////////
std::string const RestVocbaseBaseHandler::GHARIAL_PATH = "/_api/gharial";
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief endpoint path /// @brief endpoint path
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -103,6 +103,12 @@ class RestVocbaseBaseHandler : public RestBaseHandler {
static std::string const EDGES_PATH; static std::string const EDGES_PATH;
//////////////////////////////////////////////////////////////////////////////
/// @brief gharial graph api path
//////////////////////////////////////////////////////////////////////////////
static std::string const GHARIAL_PATH;
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/// @brief endpoint 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 /// Copyright holder is ArangoDB GmbH, Cologne, Germany
/// ///
/// @author Michael Hackstein /// @author Heiko Kernbach
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#ifndef ARANGOD_VOCBASE_GRAPHS_H #ifndef ARANGOD_V8_SERVER_V8_GENERAL_GRAPH_H
#define ARANGOD_VOCBASE_GRAPHS_H 1 #define ARANGOD_V8_SERVER_V8_GENERAL_GRAPH_H 1
#include "VocBase/vocbase.h" #include <v8.h>
namespace arangodb { struct TRI_vocbase_t;
namespace aql { struct TRI_v8_global_t;
class Graph;
}
namespace transaction { void TRI_InitV8GeneralGraph(v8::Handle<v8::Context> context,
class Context; TRI_vocbase_t* vocbase, TRI_v8_global_t* v8g,
} v8::Isolate* isolate);
////////////////////////////////////////////////////////////////////////////////
/// @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
#endif #endif

View File

@ -79,6 +79,7 @@
#include "V8Server/v8-views.h" #include "V8Server/v8-views.h"
#include "V8Server/v8-voccursor.h" #include "V8Server/v8-voccursor.h"
#include "V8Server/v8-vocindex.h" #include "V8Server/v8-vocindex.h"
#include "V8Server/v8-general-graph.h"
#include "VocBase/KeyGenerator.h" #include "VocBase/KeyGenerator.h"
#include "VocBase/LogicalCollection.h" #include "VocBase/LogicalCollection.h"
#include "VocBase/Methods/Databases.h" #include "VocBase/Methods/Databases.h"
@ -2068,6 +2069,7 @@ void TRI_InitV8VocBridge(
TRI_InitV8Collections(context, &vocbase, v8g, isolate, ArangoNS); TRI_InitV8Collections(context, &vocbase, v8g, isolate, ArangoNS);
TRI_InitV8Views(context, &vocbase, v8g, isolate, ArangoNS); TRI_InitV8Views(context, &vocbase, v8g, isolate, ArangoNS);
TRI_InitV8Users(context, &vocbase, v8g, isolate); TRI_InitV8Users(context, &vocbase, v8g, isolate);
TRI_InitV8GeneralGraph(context, &vocbase, v8g, isolate);
TRI_InitV8cursor(context, v8g); 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); 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.appendJsonEncoded(oldName.c_str(), oldName.size());
buffer.appendChar(','); buffer.appendChar(',');
buffer.appendJsonEncoded(newName.c_str(), newName.size()); buffer.appendJsonEncoded(newName.c_str(), newName.size());

View File

@ -655,7 +655,7 @@
} else { } else {
newCollectionObject.isSmart = true; newCollectionObject.isSmart = true;
newCollectionObject.options = { newCollectionObject.options = {
numberOfShards: $('#new-numberOfShards').val(), numberOfShards: parseInt($('#new-numberOfShards').val()),
smartGraphAttribute: $('#new-smartGraphAttribute').val(), smartGraphAttribute: $('#new-smartGraphAttribute').val(),
replicationFactor: parseInt($('#new-replicationFactor').val()) replicationFactor: parseInt($('#new-replicationFactor').val())
}; };
@ -664,15 +664,15 @@
if (frontendConfig.isCluster) { if (frontendConfig.isCluster) {
if ($('#general-numberOfShards').val().length > 0) { if ($('#general-numberOfShards').val().length > 0) {
newCollectionObject.options = { newCollectionObject.options = {
numberOfShards: $('#general-numberOfShards').val() numberOfShards: parseInt($('#general-numberOfShards').val())
}; };
} }
if ($('#general-replicationFactor').val().length > 0) { if ($('#general-replicationFactor').val().length > 0) {
if (newCollectionObject.options) { if (newCollectionObject.options) {
newCollectionObject.options.replicationFactor = $('#general-replicationFactor').val(); newCollectionObject.options.replicationFactor = parseInt($('#general-replicationFactor').val());
} else { } else {
newCollectionObject.options = { 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 // / @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 arangodb = require('@arangodb');
const request = require('@arangodb/request'); const request = require('@arangodb/request');
const ERRORS = arangodb.errors; const ERRORS = arangodb.errors;
const db = arangodb.db; const db = arangodb.db;
const wait = require('internal').wait; const internal = require('internal');
const extend = require('lodash').extend; const wait = internal.wait;
describe('_api/gharial', () => { describe('_api/gharial', () => {
@ -314,4 +316,296 @@ describe('_api/gharial', () => {
expect(db._collection(vName)).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', () => {
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_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_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_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_UNKNOWN" : { "code" : 1950, "message" : "unknown session" },
"ERROR_SESSION_EXPIRED" : { "code" : 1951, "message" : "session expired" }, "ERROR_SESSION_EXPIRED" : { "code" : 1951, "message" : "session expired" },
"SIMPLE_CLIENT_UNKNOWN_ERROR" : { "code" : 2000, "message" : "unknown client error" }, "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_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_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_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_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_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" }, "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; let res = false;
if (col === null && !noCreate) { if (col === null && !noCreate) {
if (type === ArangoCollection.TYPE_DOCUMENT) { if (type === ArangoCollection.TYPE_DOCUMENT) {
if (options) { col = db._create(name, options);
col = db._create(name, options);
} else {
col = db._create(name);
}
} else { } else {
if (options) { col = db._createEdgeCollection(name, options);
col = db._createEdgeCollection(name, options);
} else {
col = db._createEdgeCollection(name);
}
} }
res = true; res = true;
} else if (!(col instanceof ArangoCollection)) { } 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 // / internal helper to sort a graph's edge definitions
// ////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////
var sortEdgeDefinition = function (edgeDefinition) { var sortEdgeDefinitionInplace = function (edgeDefinition) {
edgeDefinition.from = edgeDefinition.from.sort(); edgeDefinition.from.sort();
edgeDefinition.to = edgeDefinition.to.sort(); edgeDefinition.to.sort();
return edgeDefinition; 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 // / @brief create a new graph
// ////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////
@ -731,6 +731,13 @@ var checkIfMayBeDropped = function (colName, graphName, graphs) {
return result; 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. // @brief Class Graph. Defines a graph in the Database.
class Graph { class Graph {
constructor (info) { constructor (info) {
@ -762,7 +769,7 @@ class Graph {
var self = this; var self = this;
// Create Hidden Properties // Create Hidden Properties
createHiddenProperty(this, '__useBuiltIn', useBuiltIn); createHiddenProperty(this, '__useBuiltIn', useBuiltIn);
createHiddenProperty(this, '__name', info._key); createHiddenProperty(this, '__name', info._key || info.name);
createHiddenProperty(this, '__vertexCollections', vertexCollections); createHiddenProperty(this, '__vertexCollections', vertexCollections);
createHiddenProperty(this, '__edgeCollections', edgeCollections); createHiddenProperty(this, '__edgeCollections', edgeCollections);
createHiddenProperty(this, '__edgeDefinitions', info.edgeDefinitions); createHiddenProperty(this, '__edgeDefinitions', info.edgeDefinitions);
@ -779,7 +786,7 @@ class Graph {
// Create Hidden Functions // Create Hidden Functions
createHiddenProperty(this, '__updateBindCollections', updateBindCollections); createHiddenProperty(this, '__updateBindCollections', updateBindCollections);
createHiddenProperty(this, '__sortEdgeDefinition', sortEdgeDefinition); createHiddenProperty(this, '__sortEdgeDefinition', sortEdgeDefinitionInplace);
updateBindCollections(self); updateBindCollections(self);
} }
@ -1551,226 +1558,6 @@ class Graph {
return result; 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 // / @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; err.errorMessage = arangodb.errors.ERROR_GRAPH_CREATE_MALFORMED_EDGE_DEFINITION.message;
throw err; throw err;
} }
edgeDefinitions = _.cloneDeep(edgeDefinitions);
// check, if a collection is already used in a different edgeDefinition // check, if a collection is already used in a different edgeDefinition
let tmpCollections = []; let tmpCollections = [];
let tmpEdgeDefinitions = {}; let tmpEdgeDefinitions = {};
@ -2039,7 +1829,7 @@ exports._create = function (graphName, edgeDefinitions, orphanCollections, optio
(sGED) => { (sGED) => {
var col = sGED.collection; var col = sGED.collection;
if (tmpCollections.indexOf(col) !== -1) { if (tmpCollections.indexOf(col) !== -1) {
if (JSON.stringify(sGED) !== JSON.stringify(tmpEdgeDefinitions[col])) { if (!edgeDefinitionsEqual(sGED, tmpEdgeDefinitions[col])) {
let err = new ArangoError(); let err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_COLLECTION_USE_IN_MULTI_GRAPHS.code; err.errorNum = arangodb.errors.ERROR_GRAPH_COLLECTION_USE_IN_MULTI_GRAPHS.code;
err.errorMessage = col + ' ' + err.errorMessage = col + ' ' +
@ -2076,12 +1866,7 @@ exports._create = function (graphName, edgeDefinitions, orphanCollections, optio
} }
); );
edgeDefinitions.forEach( edgeDefinitions.forEach(sortEdgeDefinitionInplace);
(eD, index) => {
var tmp = sortEdgeDefinition(eD);
edgeDefinitions[index] = tmp;
}
);
orphanCollections = orphanCollections.sort(); orphanCollections = orphanCollections.sort();
var data = gdb.save({ var data = gdb.save({
@ -2089,7 +1874,7 @@ exports._create = function (graphName, edgeDefinitions, orphanCollections, optio
'edgeDefinitions': edgeDefinitions, 'edgeDefinitions': edgeDefinitions,
'_key': graphName, '_key': graphName,
'numberOfShards': options.numberOfShards || 1, 'numberOfShards': options.numberOfShards || 1,
'replicationFactor': options.replicationFactor || 1 'replicationFactor': options.replicationFactor || 1,
}, options); }, options);
data.orphanCollections = orphanCollections; data.orphanCollections = orphanCollections;
data.edgeDefinitions = edgeDefinitions; data.edgeDefinitions = edgeDefinitions;
@ -2257,82 +2042,82 @@ exports._listObjects = function () {
exports._registerCompatibilityFunctions = function () { exports._registerCompatibilityFunctions = function () {
const aqlfunctions = require('@arangodb/aql/functions'); const aqlfunctions = require('@arangodb/aql/functions');
aqlfunctions.register('arangodb::GRAPH_EDGES', function (graphName, vertexExample, options) { 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); var g = gm._graph(graphName);
return g._edges(vertexExample, options); return g._edges(vertexExample, options);
}, false); }, false);
aqlfunctions.register('arangodb::GRAPH_VERTICES', function (graphName, vertexExample, options) { 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); var g = gm._graph(graphName);
return g._vertices(vertexExample, options); return g._vertices(vertexExample, options);
}, false); }, false);
aqlfunctions.register('arangodb::GRAPH_NEIGHBORS', function (graphName, vertexExample, options) { 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); var g = gm._graph(graphName);
return g._neighbors(vertexExample, options); return g._neighbors(vertexExample, options);
}, false); }, false);
aqlfunctions.register('arangodb::GRAPH_COMMON_NEIGHBORS', function (graphName, vertex1Example, vertex2Example, options1, options2) { 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); var g = gm._graph(graphName);
return g._commonNeighbors(vertex1Example, vertex2Example, options1, options2); return g._commonNeighbors(vertex1Example, vertex2Example, options1, options2);
}, false); }, false);
aqlfunctions.register('arangodb::GRAPH_COMMON_PROPERTIES', function (graphName, vertex1Example, vertex2Example, options) { 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); var g = gm._graph(graphName);
return g._commonProperties(vertex1Example, vertex2Example, options); return g._commonProperties(vertex1Example, vertex2Example, options);
}, false); }, false);
aqlfunctions.register('arangodb::GRAPH_PATHS', function (graphName, options) { 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); var g = gm._graph(graphName);
return g._paths(options); return g._paths(options);
}, false); }, false);
aqlfunctions.register('arangodb::GRAPH_SHORTEST_PATH', function (graphName, startVertexExample, edgeVertexExample, options) { 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); var g = gm._graph(graphName);
return g._shortestPath(startVertexExample, edgeVertexExample, options); return g._shortestPath(startVertexExample, edgeVertexExample, options);
}, false); }, false);
aqlfunctions.register('arangodb::GRAPH_DISTANCE_TO', function (graphName, startVertexExample, edgeVertexExample, options) { 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); var g = gm._graph(graphName);
return g._distanceTo(startVertexExample, edgeVertexExample, options); return g._distanceTo(startVertexExample, edgeVertexExample, options);
}, false); }, false);
aqlfunctions.register('arangodb::GRAPH_ABSOLUTE_ECCENTRICITY', function (graphName, vertexExample, options) { 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); var g = gm._graph(graphName);
return g._absoluteEccentricity(vertexExample, options); return g._absoluteEccentricity(vertexExample, options);
}, false); }, false);
aqlfunctions.register('arangodb::GRAPH_ECCENTRICITY', function (graphName, options) { 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); var g = gm._graph(graphName);
return g._eccentricity(options); return g._eccentricity(options);
}, false); }, false);
aqlfunctions.register('arangodb::GRAPH_ABSOLUTE_CLOSENESS', function (graphName, vertexExample, options) { 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); var g = gm._graph(graphName);
return g._farness(vertexExample, options); return g._farness(vertexExample, options);
}, false); }, false);
aqlfunctions.register('arangodb::GRAPH_CLOSENESS', function (graphName, options) { 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); var g = gm._graph(graphName);
return g._closeness(options); return g._closeness(options);
}, false); }, false);
aqlfunctions.register('arangodb::GRAPH_ABSOLUTE_BETWEENNESS', function (graphName, vertexExample, options) { 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); var g = gm._graph(graphName);
return g._absoluteBetweenness(vertexExample, options); return g._absoluteBetweenness(vertexExample, options);
}, false); }, false);
aqlfunctions.register('arangodb::GRAPH_BETWEENNESS', function (graphName, options) { 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); var g = gm._graph(graphName);
return g._betweenness(options); return g._betweenness(options);
}, false); }, false);
aqlfunctions.register('arangodb::GRAPH_RADIUS', function (graphName, options) { 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); var g = gm._graph(graphName);
return g._radius(options); return g._radius(options);
}, false); }, false);
aqlfunctions.register('arangodb::GRAPH_DIAMETER', function (graphName, options) { 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); var g = gm._graph(graphName);
return g._diameter(options); return g._diameter(options);
}, false); }, false);

View File

@ -57,6 +57,8 @@ function GeneralGraphCreationSuite() {
var vn3 = "UnitTestVertices3"; var vn3 = "UnitTestVertices3";
var vn4 = "UnitTestVertices4"; var vn4 = "UnitTestVertices4";
var gn = "UnitTestGraph"; var gn = "UnitTestGraph";
var gn1 = "UnitTestGraph1";
var gn2 = "UnitTestGraph2";
var edgeDef = graph._edgeDefinitions( var edgeDef = graph._edgeDefinitions(
graph._relation(rn, [vn1], [vn1]), graph._relation(rn, [vn1], [vn1]),
graph._relation(rn1, graph._relation(rn1,
@ -76,6 +78,14 @@ function GeneralGraphCreationSuite() {
vc5 = "UnitTestEdgeDefDeleteVertexCol5", vc5 = "UnitTestEdgeDefDeleteVertexCol5",
vc6 = "UnitTestEdgeDefDeleteVertexCol6"; vc6 = "UnitTestEdgeDefDeleteVertexCol6";
var sortEdgeDefinition = function(edgeDefinition) {
return {
collection: edgeDefinition.collection,
from: edgeDefinition.from.slice().sort(),
to: edgeDefinition.to.slice().sort()
}
};
return { return {
setUp: function() { setUp: function() {
@ -111,6 +121,12 @@ function GeneralGraphCreationSuite() {
graph._drop(gN2, true); graph._drop(gN2, true);
} catch(ignore) { } 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 () { test_create : function () {
if (db._collection("_graphs").exists(gn)) { if (db._collection("_graphs").exists(gn)) {
db._collection("_graphs").remove(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 () { test_get_graph : function () {
if (db._collection("_graphs").exists(gn)) { if (db._collection("_graphs").exists(gn)) {
db._collection("_graphs").remove(gn); db._collection("_graphs").remove(gn);
@ -582,11 +655,17 @@ function GeneralGraphCreationSuite() {
g1._deleteEdgeDefinition(ec1); g1._deleteEdgeDefinition(ec1);
assertEqual([dr2, dr3], g1.__edgeDefinitions); assertEqual([dr2, dr3], g1.__edgeDefinitions);
assertEqual([vc1, vc2], g1._orphanCollections()); 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); assertTrue(db._collection(ec1) !== null);
g1._deleteEdgeDefinition(ec2); g1._deleteEdgeDefinition(ec2);
assertEqual([dr3], g1.__edgeDefinitions); 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); assertTrue(db._collection(ec2) !== null);
}, },
@ -601,14 +680,20 @@ function GeneralGraphCreationSuite() {
g1._deleteEdgeDefinition(ec1, true); g1._deleteEdgeDefinition(ec1, true);
assertEqual([dr2, dr3], g1.__edgeDefinitions); assertEqual([dr2, dr3], g1.__edgeDefinitions);
assertEqual([vc1, vc2], g1._orphanCollections()); 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); assertTrue(db._collection(ec1) === null);
g1._deleteEdgeDefinition(ec2, true); g1._deleteEdgeDefinition(ec2, true);
assertEqual([dr3], g1.__edgeDefinitions); 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); assertTrue(db._collection(ec2) === null);
}, },
test_extendEdgeDefinitionFromExistingGraph1: function() { test_extendEdgeDefinitionFromExistingGraph1: function() {
try { try {
@ -622,10 +707,11 @@ function GeneralGraphCreationSuite() {
try { try {
g1._extendEdgeDefinitions(dr2); g1._extendEdgeDefinitions(dr2);
fail();
} catch (e) { } catch (e) {
assertEqual( assertEqual(
e.errorMessage, 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]); g1 = graph._create(gN1, [dr1]);
graph._create(gN2, [dr2]); 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); assertEqual([dr1], g1.__edgeDefinitions);
g1._addVertexCollection(vc3); g1._addVertexCollection(vc3);
assertEqual([vc3], g1._orphanCollections()); assertEqual([vc3], g1._orphanCollections());
assertEqual([vc3], loadG1()._orphanCollections());
g1._extendEdgeDefinitions(dr3); 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([], g1._orphanCollections());
assertEqual([], loadG1()._orphanCollections());
g1._extendEdgeDefinitions(dr2); 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() { test_extendEdgeDefinitionFromExistingGraph4: function() {
@ -705,15 +805,22 @@ function GeneralGraphCreationSuite() {
dr2 = graph._relation(ec2, [vc4, vc3, vc1, vc2], [vc4, vc3, vc1, vc2]), dr2 = graph._relation(ec2, [vc4, vc3, vc1, vc2], [vc4, vc3, vc1, vc2]),
g1 = graph._create(gN1, [dr1]); g1 = graph._create(gN1, [dr1]);
const loadG1 = () => graph._graph(gN1);
g1._extendEdgeDefinitions(dr2); g1._extendEdgeDefinitions(dr2);
dr1 = sortEdgeDefinition(dr1);
dr2 = sortEdgeDefinition(dr2);
assertEqual([dr1, dr2], g1.__edgeDefinitions); 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.from, [vc1, vc2, vc3, vc4]);
assertEqual(edgeDefinition.to, [vc1, vc2, vc3, vc4]); assertEqual(edgeDefinition.to, [vc1, vc2, vc3, vc4]);
}, },
test_editEdgeDefinitionFromExistingGraph1: function() { test_editEdgeDefinitionFromExistingGraph1: function() {
var dr1 = graph._relation(ec1, [vc1], [vc1, vc2]), var dr1 = graph._relation(ec1, [vc1], [vc1, vc2]),
dr2 = graph._relation(ec2, [vc3], [vc4, vc5]), dr2 = graph._relation(ec2, [vc3], [vc4, vc5]),
@ -732,37 +839,42 @@ function GeneralGraphCreationSuite() {
test_editEdgeDefinitionFromExistingGraph2: function() { test_editEdgeDefinitionFromExistingGraph2: function() {
var dr1 = graph._relation(ec1, [vc1, vc2], [vc3, vc4]), var dr1 = graph._relation(ec1, _.cloneDeep([vc1, vc2]), _.cloneDeep([vc3, vc4])),
dr2 = graph._relation(ec2, [vc1], [vc4]), dr2 = graph._relation(ec2, _.cloneDeep([vc1]), _.cloneDeep([vc4])),
dr3 = graph._relation(ec1, [vc5], [vc5]), dr3 = graph._relation(ec1, _.cloneDeep([vc5]), _.cloneDeep([vc5])),
g1 = graph._create(gN1, [dr1, dr2]), g1 = graph._create(gN1, _.cloneDeep([dr1, dr2])),
g2 = graph._create(gN2, [dr1]); 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, dr2], g1.__edgeDefinitions);
assertEqual([dr3], g2.__edgeDefinitions); assertEqual([dr3], g2.__edgeDefinitions);
g2 = graph._graph(gN2);
assertEqual(g1._orphanCollections().sort(), [vc2, vc3].sort()); assertEqual(g1._orphanCollections().sort(), [vc2, vc3].sort());
assertEqual(g2._orphanCollections().sort(), [vc1, vc2, vc3, vc4].sort()); assertEqual(g2._orphanCollections().sort(), [vc1, vc2, vc3, vc4].sort());
}, },
test_editEdgeDefinitionFromExistingGraph3: function() { test_editEdgeDefinitionFromExistingGraph3: function() {
var dr1 = graph._relation(ec1, [vc1], [vc1, vc2]), var dr1 = graph._relation(ec1, _.cloneDeep([vc1]), _.cloneDeep([vc1, vc2])),
dr2 = graph._relation(ec1, [vc3], [vc4, vc5]), dr2 = graph._relation(ec1, _.cloneDeep([vc3]), _.cloneDeep([vc4, vc5])),
dr3 = graph._relation(ec2, [vc2], [vc2, vc3]), dr3 = graph._relation(ec2, _.cloneDeep([vc2]), _.cloneDeep([vc2, vc3])),
g1 = graph._create(gN1, [dr1, dr3]), g1 = graph._create(gN1, _.cloneDeep([dr1, dr3])),
g2 = graph._create(gN2, [dr1]); g2 = graph._create(gN2, _.cloneDeep([dr1]));
g1._addVertexCollection(vc4); g1._addVertexCollection(vc4);
g2._addVertexCollection(vc5); g2._addVertexCollection(vc5);
g2._addVertexCollection(vc6); 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, dr3], g1.__edgeDefinitions);
assertEqual([dr2], g2.__edgeDefinitions); assertEqual([dr2], g2.__edgeDefinitions);
g2 = graph._graph(gN2);
assertEqual([vc1], g1._orphanCollections()); assertEqual([vc1], g1._orphanCollections());
assertEqual(g2._orphanCollections().sort(), [vc1, vc2, vc6].sort()); assertEqual(g2._orphanCollections().sort(), [vc1, vc2, vc6].sort());
@ -919,14 +1031,17 @@ function EdgesAndVerticesSuite() {
var myGraphName = unitTestGraphName + "2"; var myGraphName = unitTestGraphName + "2";
var myEdgeColName = "unitTestEdgeCollection4711"; var myEdgeColName = "unitTestEdgeCollection4711";
var myVertexColName = vc1; var myVertexColName = vc1;
graph._create( graph._create(
myGraphName, myGraphName,
graph._edgeDefinitions( graph._edgeDefinitions(
graph._relation(myEdgeColName, myVertexColName, myVertexColName) graph._relation(myEdgeColName, myVertexColName, myVertexColName)
) )
); );
graph._drop(myGraphName, true); graph._drop(myGraphName, true);
assertFalse(graph._exists(myGraphName)); assertFalse(graph._exists(myGraphName));
db._flushCache();
assertTrue(db._collection(myVertexColName) !== null); assertTrue(db._collection(myVertexColName) !== null);
assertTrue(db._collection(myEdgeColName) === null); assertTrue(db._collection(myEdgeColName) === null);
}, },
@ -1013,11 +1128,9 @@ function EdgesAndVerticesSuite() {
graph._relation(myED, myVD2, myVD2) graph._relation(myED, myVD2, myVD2)
) )
); );
fail();
} catch (e) { } catch (e) {
assertEqual( assertEqual(e.errorNum, arangodb.errors.ERROR_GRAPH_COLLECTION_MULTI_USE.code)
e.errorMessage,
arangodb.errors.ERROR_GRAPH_COLLECTION_MULTI_USE.message
);
} }
assertFalse(graph._exists(myGraphName)); assertFalse(graph._exists(myGraphName));
assertTrue(db._collection(myVD1) === null); assertTrue(db._collection(myVD1) === null);
@ -1144,14 +1257,14 @@ function EdgesAndVerticesSuite() {
g[ec1].save(vertexId1, vertexId2, {}); g[ec1].save(vertexId1, vertexId2, {});
fail(); fail();
} catch (e) { } catch (e) {
assertEqual(e.errorNum, 1906); assertEqual(e.errorNum, arangodb.errors.ERROR_GRAPH_INVALID_EDGE.code);
} }
try { try {
g[ec1].insert(vertexId1, vertexId2, {}); g[ec1].insert(vertexId1, vertexId2, {});
fail(); fail();
} catch (e) { } catch (e) {
assertEqual(e.errorNum, 1906); assertEqual(e.errorNum, arangodb.errors.ERROR_GRAPH_INVALID_EDGE.code);
} }
g[vc1].remove(vertexId1); g[vc1].remove(vertexId1);
@ -1764,9 +1877,10 @@ function OrphanCollectionSuite() {
g1._addVertexCollection(vC4, false); g1._addVertexCollection(vC4, false);
} catch (e) { } catch (e) {
assertEqual(e.errorNum, ERRORS.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.code); 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); assertTrue(db._collection(vC4) === null);
g1 = graph._graph(gN1);
assertEqual(g1._orphanCollections(), []); assertEqual(g1._orphanCollections(), []);
}, },
@ -1786,7 +1900,7 @@ function OrphanCollectionSuite() {
g1._addVertexCollection(vC1); g1._addVertexCollection(vC1);
} catch (e) { } catch (e) {
assertEqual(e.errorNum, ERRORS.ERROR_GRAPH_COLLECTION_USED_IN_EDGE_DEF.code); 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 { try {
g1._removeVertexCollection(name); g1._removeVertexCollection(name);
} catch (e) { } catch (e) {
assertEqual(e.errorNum, ERRORS.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.code); assertEqual(e.errorNum, ERRORS.ERROR_GRAPH_NOT_IN_ORPHAN_COLLECTION.code);
assertEqual(e.errorMessage, ERRORS.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.message); assertEqual(e.errorMessage, ERRORS.ERROR_GRAPH_NOT_IN_ORPHAN_COLLECTION.message);
} }
}, },
test_removeVertexCollection2: function() { test_removeVertexCollection2: function() {
g1._addVertexCollection(vC4, true); g1._addVertexCollection(vC4, true);
g1._addVertexCollection(vC5, true); g1._addVertexCollection(vC5, true);
assertEqual(g1._orphanCollections(), [vC4, vC5]); assertEqual(g1._orphanCollections().sort(), [vC4, vC5].sort());
g1._removeVertexCollection(vC4, false); g1._removeVertexCollection(vC4, false);
assertTrue(db._collection(vC4) !== null); assertTrue(db._collection(vC4) !== null);
assertEqual(g1._orphanCollections(), [vC5]); g1 = graph._graph(gN1);
assertEqual(g1._orphanCollections().sort(), [vC5].sort());
try { try {
g1._removeVertexCollection(vC4, true); g1._removeVertexCollection(vC4, true);
} catch (e) { } catch (e) {
@ -1822,6 +1937,7 @@ function OrphanCollectionSuite() {
graph._drop(gN1, true); graph._drop(gN1, true);
assertTrue(db._collection(vC3) !== null); assertTrue(db._collection(vC3) !== null);
graph._drop(gN2, true); graph._drop(gN2, true);
db._flushCache();
assertTrue(db._collection(vC3) === null); assertTrue(db._collection(vC3) === null);
}, },
@ -1832,6 +1948,7 @@ function OrphanCollectionSuite() {
graph._drop(gN2, true); graph._drop(gN2, true);
assertTrue(db._collection(vC3) !== null); assertTrue(db._collection(vC3) !== null);
graph._drop(gN1, true); graph._drop(gN1, true);
db._flushCache();
assertTrue(db._collection(vC3) === null); assertTrue(db._collection(vC3) === null);
}, },
@ -1843,6 +1960,7 @@ function OrphanCollectionSuite() {
graph._drop(gN1, true); graph._drop(gN1, true);
assertTrue(db._collection(vC4) !== null); assertTrue(db._collection(vC4) !== null);
graph._drop(gN2, true); graph._drop(gN2, true);
db._flushCache();
assertTrue(db._collection(vC4) === null); assertTrue(db._collection(vC4) === null);
}, },

View File

@ -62,6 +62,27 @@
exports.ArangoUsers = global.ArangoUsers; exports.ArangoUsers = global.ArangoUsers;
delete 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 // / @brief ArangoDatabase

View File

@ -47,8 +47,7 @@ const isZipBuffer = require('@arangodb/util').isZipBuffer;
const SYSTEM_SERVICE_MOUNTS = [ const SYSTEM_SERVICE_MOUNTS = [
'/_admin/aardvark', // Admin interface. '/_admin/aardvark', // Admin interface.
'/_api/foxx', // Foxx management API. '/_api/foxx' // Foxx management API.
'/_api/gharial' // General_Graph API.
]; ];
const GLOBAL_SERVICE_MAP = new Map(); 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: public:
Result() : _errorNumber(TRI_ERROR_NO_ERROR) {} Result() : _errorNumber(TRI_ERROR_NO_ERROR) {}
Result(bool avoidCastingErrors) = delete;
Result(int errorNumber) Result(int errorNumber)
: _errorNumber(errorNumber){ : _errorNumber(errorNumber){
if (errorNumber != TRI_ERROR_NO_ERROR) { if (errorNumber != TRI_ERROR_NO_ERROR) {

View File

@ -64,12 +64,18 @@ std::string const StaticStrings::ReplaceExisting("replaceExisting");
std::string const StaticStrings::OverWrite("overwrite"); std::string const StaticStrings::OverWrite("overwrite");
// replication headers // replication headers
std::string const StaticStrings::ReplicationHeaderCheckMore("x-arango-replication-checkmore"); std::string const StaticStrings::ReplicationHeaderCheckMore(
std::string const StaticStrings::ReplicationHeaderLastIncluded("x-arango-replication-lastincluded"); "x-arango-replication-checkmore");
std::string const StaticStrings::ReplicationHeaderLastScanned("x-arango-replication-lastscanned"); std::string const StaticStrings::ReplicationHeaderLastIncluded(
std::string const StaticStrings::ReplicationHeaderLastTick("x-arango-replication-lasttick"); "x-arango-replication-lastincluded");
std::string const StaticStrings::ReplicationHeaderFromPresent("x-arango-replication-frompresent"); std::string const StaticStrings::ReplicationHeaderLastScanned(
std::string const StaticStrings::ReplicationHeaderActive("x-arango-replication-active"); "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 // database and collection names
std::string const StaticStrings::SystemDatabase("_system"); 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::MimeTypeVPack("application/x-velocypack");
std::string const StaticStrings::MultiPartContentType("multipart/form-data"); 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 // misc strings
std::string const StaticStrings::LastValue("lastValue"); std::string const StaticStrings::LastValue("lastValue");

View File

@ -148,6 +148,26 @@ class StaticStrings {
static std::string const MimeTypeVPack; static std::string const MimeTypeVPack;
static std::string const MultiPartContentType; 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 // misc strings
static std::string const LastValue; 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_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_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_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 ## 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_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_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_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 ## 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_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_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_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_UNKNOWN, "unknown session");
REG_ERROR(ERROR_SESSION_EXPIRED, "session expired"); REG_ERROR(ERROR_SESSION_EXPIRED, "session expired");
REG_ERROR(SIMPLE_CLIENT_UNKNOWN_ERROR, "unknown client error"); 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_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_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_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_FAILED, "error during cluster repairs");
REG_ERROR(ERROR_CLUSTER_REPAIRS_NOT_ENOUGH_HEALTHY, "not enough (healthy) db servers"); 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"); 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. /// The requested graph has no edge collections.
constexpr int TRI_ERROR_GRAPH_EMPTY = 1940; 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 /// 1950: ERROR_SESSION_UNKNOWN
/// "unknown session" /// "unknown session"
/// Will be raised when an invalid/unknown session id is passed to the server. /// 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. /// All system attributes are forbidden.
constexpr int TRI_ERROR_ILLEGAL_SMART_GRAPH_ATTRIBUTE = 4004; 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 /// 5000: ERROR_CLUSTER_REPAIRS_FAILED
/// "error during cluster repairs" /// "error during cluster repairs"
/// General error during cluster repairs /// General error during cluster repairs

View File

@ -49,6 +49,39 @@ enum class RequestType {
ILLEGAL // must be last 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 { enum class ContentType {
CUSTOM, // use Content-Type from _headers CUSTOM, // use Content-Type from _headers
JSON, // application/json JSON, // application/json
@ -122,6 +155,112 @@ enum class ResponseCode {
BANDWIDTH_LIMIT_EXCEEDED = 509, BANDWIDTH_LIMIT_EXCEEDED = 509,
NOT_EXTENDED = 510 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 #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_WRONG_COLLECTION_TYPE_VERTEX:
case TRI_ERROR_GRAPH_NOT_IN_ORPHAN_COLLECTION: case TRI_ERROR_GRAPH_NOT_IN_ORPHAN_COLLECTION:
case TRI_ERROR_GRAPH_COLLECTION_USED_IN_EDGE_DEF: 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_STRING:
case TRI_ERROR_GRAPH_INVALID_EXAMPLE_ARRAY_OBJECT: case TRI_ERROR_GRAPH_INVALID_EXAMPLE_ARRAY_OBJECT:
case TRI_ERROR_GRAPH_INVALID_NUMBER_OF_ARGUMENTS: 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_VERTEX_COL_DOES_NOT_EXIST:
case TRI_ERROR_GRAPH_NO_GRAPH_COLLECTION: case TRI_ERROR_GRAPH_NO_GRAPH_COLLECTION:
case TRI_ERROR_QUEUE_UNKNOWN: case TRI_ERROR_QUEUE_UNKNOWN:
case TRI_ERROR_GRAPH_EDGE_COLLECTION_NOT_USED:
return ResponseCode::NOT_FOUND; return ResponseCode::NOT_FOUND;
case TRI_ERROR_CLUSTER_SHARD_LEADER_REFUSES_REPLICATION: 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(), VocbaseTempl(),
EnvTempl(), EnvTempl(),
UsersTempl(), UsersTempl(),
GeneralGraphModuleTempl(),
GeneralGraphTempl(),
#ifdef USE_ENTERPRISE
SmartGraphTempl(),
#endif
BufferTempl(), BufferTempl(),

View File

@ -363,6 +363,19 @@ struct TRI_v8_global_t {
/// @brief users template /// @brief users template
v8::Persistent<v8::ObjectTemplate> UsersTempl; 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 /// @brief Buffer template
v8::Persistent<v8::FunctionTemplate> BufferTempl; v8::Persistent<v8::FunctionTemplate> BufferTempl;

File diff suppressed because it is too large Load Diff