mirror of https://gitee.com/bigwinds/arangodb
Solved conflict
This commit is contained in:
commit
fc5f10b3ed
15
CHANGELOG
15
CHANGELOG
|
@ -1,5 +1,18 @@
|
|||
v1.4
|
||||
------
|
||||
----
|
||||
|
||||
* changed the HTTP return code from 400 to 404 for some cases when there is a referral
|
||||
to a non-existing collection or document.
|
||||
|
||||
* introduced error code 1909 `too many iterations` that is thrown when graph traversals
|
||||
hit the `maxIterations` threshold.
|
||||
|
||||
* optionally limit traversals to a certain number of iterations
|
||||
the limitation can be achieved via the traversal API by setting the `maxIterations`
|
||||
attribute, and also via the AQL `TRAVERSAL` and `TRAVERSAL_TREE` functions by setting
|
||||
the same attribute. If traversals are not limited by the end user, a server-defined
|
||||
limit for `maxIterations` may be used to prevent server-side traversals from running
|
||||
endlessly.
|
||||
|
||||
* added graph traversal API at `/_api/traversal`
|
||||
|
||||
|
|
|
@ -1277,6 +1277,10 @@ Example calls:
|
|||
- `minDepth`: Minimum path depths for vertices to be included. This can be used to
|
||||
include only vertices in the result that are found after a certain minimum depth.
|
||||
Defaults to 0.
|
||||
- `maxIterations`: Maximum number of iterations in each traversal. This number can be
|
||||
set to prevent endless loops in traversal of cyclic graphs. When a traversal performs
|
||||
as many iterations as the `maxIterations` value, the traversal will abort with an
|
||||
error. If `maxIterations` is not set, a server-defined value may be used.
|
||||
- `maxDepth`: Maximum path depth for sub-edges expansion. This can be used to
|
||||
limit the depth of the traversal to a sensible amount. This should especially be used
|
||||
for big graphs to limit the traversal to some sensible amount, and for graphs
|
||||
|
|
|
@ -33,10 +33,10 @@ describe ArangoDB do
|
|||
body = "{ \"query\" : \"FOR u IN unknowncollection LIMIT 2 RETURN u.n\", \"count\" : true, \"bindVars\" : {}, \"batchSize\" : 2 }"
|
||||
doc = ArangoDB.log_post("#{prefix}-unknown-collection", cmd, :body => body)
|
||||
|
||||
doc.code.should eq(400)
|
||||
doc.code.should eq(404)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(true)
|
||||
doc.parsed_response['code'].should eq(400)
|
||||
doc.parsed_response['code'].should eq(404)
|
||||
doc.parsed_response['errorNum'].should eq(1203)
|
||||
end
|
||||
|
||||
|
|
|
@ -29,10 +29,10 @@ describe ArangoDB do
|
|||
body = "{ \"query\" : \"FOR u IN unknowncollection LIMIT 2 RETURN u.n\" }"
|
||||
doc = ArangoDB.log_post("#{prefix}-unknown-collection", cmd, :body => body)
|
||||
|
||||
doc.code.should eq(400)
|
||||
doc.code.should eq(404)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(true)
|
||||
doc.parsed_response['code'].should eq(400)
|
||||
doc.parsed_response['code'].should eq(404)
|
||||
doc.parsed_response['errorNum'].should eq(1203)
|
||||
end
|
||||
|
||||
|
|
|
@ -180,10 +180,10 @@ describe ArangoDB do
|
|||
body = "{ \"collections\" : { \"write\": \"_meow\" }, \"action\" : \"function () { return 1; }\" }"
|
||||
doc = ArangoDB.log_post("#{prefix}-non-existing-collection", cmd, :body => body)
|
||||
|
||||
doc.code.should eq(400)
|
||||
doc.code.should eq(404)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(true)
|
||||
doc.parsed_response['code'].should eq(400)
|
||||
doc.parsed_response['code'].should eq(404)
|
||||
doc.parsed_response['errorNum'].should eq(1203)
|
||||
end
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ describe ArangoDB do
|
|||
|
||||
cmd = "/_api/document?collection=#{@cv}"
|
||||
[
|
||||
"World", "Nothing", "Europe", "Asia", "America", "Australia", "Antarctica", "Africa", "Blackhole",
|
||||
"World", "Nothing", "Europe", "Asia", "America", "Australia", "Antarctica", "Africa", "Blackhole", "Blackhole2",
|
||||
"DE", "FR", "GB", "IE", "CN", "JP", "TW", "US", "MX", "AU", "EG", "ZA", "AN",
|
||||
"London", "Paris", "Lyon", "Cologne","Dusseldorf", "Beijing", "Shanghai", "Tokyo", "Kyoto", "Taipeh", "Perth", "Sydney"
|
||||
].each do|loc|
|
||||
|
@ -44,7 +44,9 @@ describe ArangoDB do
|
|||
["Asia", "TW"],
|
||||
["America", "US"],
|
||||
["America", "MX"],
|
||||
["Australia", "AU"]
|
||||
["Australia", "AU"],
|
||||
["Blackhole", "Blackhole2"],
|
||||
["Blackhole2", "Blackhole"]
|
||||
].each do|pair|
|
||||
from = pair[0]
|
||||
to = pair[1]
|
||||
|
@ -151,7 +153,7 @@ describe ArangoDB do
|
|||
it "invalid direction" do
|
||||
body = "{ \"edgeCollection\" : \"#{@ce}\", \"startVertex\" : \"#{@cv}/World\", \"direction\" : \"foo\" }"
|
||||
doc = ArangoDB.log_post("#{prefix}-visit-invalid-direction", api, :body => body)
|
||||
|
||||
|
||||
doc.code.should eq(400)
|
||||
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
|
@ -176,6 +178,34 @@ describe ArangoDB do
|
|||
doc.parsed_response['errorNum'].should eq(500)
|
||||
end
|
||||
|
||||
################################################################################
|
||||
## traversal abortion
|
||||
################################################################################
|
||||
|
||||
it "traversal abortion, few iterations" do
|
||||
body = "{ \"edgeCollection\" : \"#{@ce}\", \"startVertex\" : \"#{@cv}/Blackhole\", \"direction\" : \"outbound\", \"uniqueness\" : { \"vertices\" : \"none\", \"edges\" : \"none\" }, \"maxIterations\" : 5 }"
|
||||
doc = ArangoDB.log_post("#{prefix}-visit-traversal-abort1", api, :body => body)
|
||||
|
||||
doc.code.should eq(500)
|
||||
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(true)
|
||||
doc.parsed_response['code'].should eq(500)
|
||||
doc.parsed_response['errorNum'].should eq(1909)
|
||||
end
|
||||
|
||||
it "traversal abortion, many iterations" do
|
||||
body = "{ \"edgeCollection\" : \"#{@ce}\", \"startVertex\" : \"#{@cv}/Blackhole\", \"direction\" : \"outbound\", \"uniqueness\" : { \"vertices\" : \"none\", \"edges\" : \"none\" }, \"maxIterations\" : 5000, \"maxDepth\" : 999999, \"visitor\" : \"\" }"
|
||||
doc = ArangoDB.log_post("#{prefix}-visit-traversal-abort2", api, :body => body)
|
||||
|
||||
doc.code.should eq(500)
|
||||
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(true)
|
||||
doc.parsed_response['code'].should eq(500)
|
||||
doc.parsed_response['errorNum'].should eq(1909)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
################################################################################
|
||||
|
|
|
@ -144,6 +144,7 @@
|
|||
"ERROR_GRAPH_INVALID_EDGE" : { "code" : 1906, "message" : "invalid edge" },
|
||||
"ERROR_GRAPH_COULD_NOT_CREATE_EDGE" : { "code" : 1907, "message" : "could not create edge" },
|
||||
"ERROR_GRAPH_COULD_NOT_CHANGE_EDGE" : { "code" : 1908, "message" : "could not change edge" },
|
||||
"ERROR_GRAPH_TOO_MANY_ITERATIONS" : { "code" : 1909, "message" : "too many iterations" },
|
||||
"ERROR_SESSION_INVALID_SESSION" : { "code" : 1951, "message" : "invalid session" },
|
||||
"ERROR_SESSION_COULD_NOT_CREATE_SESSION" : { "code" : 1952, "message" : "could not create session" },
|
||||
"ERROR_SESSION_COULD_NOT_CHANGE_SESSION" : { "code" : 1953, "message" : "could not change session" },
|
||||
|
|
|
@ -31,6 +31,7 @@ module.define("org/arangodb/graph/traversal", function(exports, module) {
|
|||
|
||||
var graph = require("org/arangodb/graph");
|
||||
var arangodb = require("org/arangodb");
|
||||
var ArangoError = arangodb.ArangoError;
|
||||
|
||||
var db = arangodb.db;
|
||||
|
||||
|
@ -661,6 +662,7 @@ function breadthFirstSearch () {
|
|||
},
|
||||
|
||||
run: function (config, result, startVertex) {
|
||||
var maxIterations = config.maxIterations, visitCounter = 0;
|
||||
var toVisit = [ { edge: null, vertex: startVertex, parentIndex: -1 } ];
|
||||
var visited = { edges: { }, vertices: { } };
|
||||
|
||||
|
@ -674,6 +676,13 @@ function breadthFirstSearch () {
|
|||
var vertex = current.vertex;
|
||||
var edge = current.edge;
|
||||
var path;
|
||||
|
||||
if (visitCounter++ > maxIterations) {
|
||||
var err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_GRAPH_TOO_MANY_ITERATIONS.code;
|
||||
err.errorMessage = arangodb.errors.ERROR_GRAPH_TOO_MANY_ITERATIONS.message;
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (current.visit === null || current.visit === undefined) {
|
||||
current.visit = false;
|
||||
|
@ -757,12 +766,20 @@ function depthFirstSearch () {
|
|||
},
|
||||
|
||||
run: function (config, result, startVertex) {
|
||||
var maxIterations = config.maxIterations, visitCounter = 0;
|
||||
var toVisit = [ { edge: null, vertex: startVertex, visit: null } ];
|
||||
var path = { edges: [ ], vertices: [ ] };
|
||||
var visited = { edges: { }, vertices: { } };
|
||||
var reverse = checkReverse(config);
|
||||
|
||||
while (toVisit.length > 0) {
|
||||
if (visitCounter++ > maxIterations) {
|
||||
var err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_GRAPH_TOO_MANY_ITERATIONS.code;
|
||||
err.errorMessage = arangodb.errors.ERROR_GRAPH_TOO_MANY_ITERATIONS.message;
|
||||
throw err;
|
||||
}
|
||||
|
||||
// peek at the top of the stack
|
||||
var current = toVisit[toVisit.length - 1];
|
||||
var vertex = current.vertex;
|
||||
|
@ -871,52 +888,150 @@ ArangoTraverser = function (config) {
|
|||
visitor: trackingVisitor,
|
||||
filter: visitAllFilter,
|
||||
expander: outboundExpander,
|
||||
datasource: null
|
||||
datasource: null,
|
||||
maxIterations: 10000,
|
||||
minDepth: 0,
|
||||
maxDepth: 256
|
||||
}, d;
|
||||
|
||||
var err;
|
||||
|
||||
if (typeof config !== "object") {
|
||||
throw "invalid configuration";
|
||||
err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_BAD_PARAMETER.code;
|
||||
err.errorMessage = arangodb.errors.ERROR_BAD_PARAMETER.message;
|
||||
throw err;
|
||||
}
|
||||
|
||||
// apply defaults
|
||||
for (d in defaults) {
|
||||
if (defaults.hasOwnProperty(d)) {
|
||||
if (! config.hasOwnProperty(d)) {
|
||||
if (! config.hasOwnProperty(d) || config[d] === undefined) {
|
||||
config[d] = defaults[d];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof config.visitor !== "function") {
|
||||
throw "invalid visitor";
|
||||
}
|
||||
|
||||
if (Array.isArray(config.filter)) {
|
||||
config.filter.forEach( function (f) {
|
||||
if (typeof f !== "function") {
|
||||
throw "invalid filter";
|
||||
function validate (value, map, param) {
|
||||
var m;
|
||||
|
||||
if (value === null || value === undefined) {
|
||||
// use first key from map
|
||||
for (m in map) {
|
||||
if (map.hasOwnProperty(m)) {
|
||||
value = m;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
value = value.toLowerCase().replace(/-/, "");
|
||||
if (map[value] !== null) {
|
||||
return map[value];
|
||||
}
|
||||
}
|
||||
for (m in map) {
|
||||
if (map.hasOwnProperty(m)) {
|
||||
if (map[m] === value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var innerFilters = config.filter.slice();
|
||||
err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_BAD_PARAMETER.code;
|
||||
err.errorMessage = "invalid value for " + param;
|
||||
throw err;
|
||||
}
|
||||
|
||||
config.uniqueness = {
|
||||
vertices: validate(config.uniqueness && config.uniqueness.vertices, {
|
||||
global: ArangoTraverser.UNIQUE_GLOBAL,
|
||||
none: ArangoTraverser.UNIQUE_NONE,
|
||||
path: ArangoTraverser.UNIQUE_PATH
|
||||
}, "uniqueness.vertices"),
|
||||
edges: validate(config.uniqueness && config.uniqueness.edges, {
|
||||
global: ArangoTraverser.UNIQUE_GLOBAL,
|
||||
none: ArangoTraverser.UNIQUE_NONE,
|
||||
path: ArangoTraverser.UNIQUE_PATH
|
||||
}, "uniqueness.edges")
|
||||
};
|
||||
|
||||
config.strategy = validate(config.strategy, {
|
||||
depthfirst: ArangoTraverser.DEPTH_FIRST,
|
||||
breadthfirst: ArangoTraverser.BREADTH_FIRST
|
||||
}, "strategy");
|
||||
|
||||
var combinedFilter = function (config, vertex, path) {
|
||||
return combineFilters(innerFilters, config, vertex, path);
|
||||
};
|
||||
config.order = validate(config.order, {
|
||||
preorder: ArangoTraverser.PRE_ORDER,
|
||||
postorder: ArangoTraverser.POST_ORDER
|
||||
}, "order");
|
||||
|
||||
config.filter = combinedFilter;
|
||||
config.itemOrder = validate(config.itemOrder, {
|
||||
forward: ArangoTraverser.FORWARD,
|
||||
backward: ArangoTraverser.BACKWARD
|
||||
}, "itemOrder");
|
||||
|
||||
|
||||
if (typeof config.visitor !== "function") {
|
||||
err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_BAD_PARAMETER.code;
|
||||
err.errorMessage = "invalid visitor function";
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (typeof config.filter !== "function") {
|
||||
throw "invalid filter";
|
||||
// prepare an array of filters
|
||||
var filters = [ ];
|
||||
if (config.minDepth !== undefined && config.minDepth >= 0) {
|
||||
filters.push(minDepthFilter);
|
||||
}
|
||||
if (config.maxDepth !== undefined && config.maxDepth > 0) {
|
||||
filters.push(maxDepthFilter);
|
||||
}
|
||||
|
||||
if (! Array.isArray(config.filter)) {
|
||||
config.filter = [ config.filter ];
|
||||
}
|
||||
|
||||
config.filter.forEach( function (f) {
|
||||
if (typeof f !== "function") {
|
||||
err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_BAD_PARAMETER.code;
|
||||
err.errorMessage = "invalid filter function";
|
||||
throw err;
|
||||
}
|
||||
|
||||
filters.push(f);
|
||||
});
|
||||
|
||||
if (filters.length === 0) {
|
||||
filters.push(visitAllFilter);
|
||||
}
|
||||
|
||||
config.filter = function (config, vertex, path) {
|
||||
return combineFilters(filters, config, vertex, path);
|
||||
};
|
||||
|
||||
if (typeof config.expander !== "function") {
|
||||
config.expander = validate(config.expander, {
|
||||
outbound: outboundExpander,
|
||||
inbound: inboundExpander,
|
||||
any: anyExpander
|
||||
}, "expander");
|
||||
}
|
||||
|
||||
if (typeof config.expander !== "function") {
|
||||
throw "invalid expander";
|
||||
err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_BAD_PARAMETER.code;
|
||||
err.errorMessage = "invalid expander function";
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (typeof config.datasource !== "object") {
|
||||
throw "invalid datasource";
|
||||
err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_BAD_PARAMETER.code;
|
||||
err.errorMessage = "invalid datasource";
|
||||
throw err;
|
||||
}
|
||||
|
||||
this.config = config;
|
||||
|
|
|
@ -65,32 +65,6 @@ function notFound (req, res, code, message) {
|
|||
message);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief validate and translate the argument given in value
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function validateArg (value, map) {
|
||||
if (value === null || value === undefined) {
|
||||
var m;
|
||||
// use first key from map
|
||||
for (m in map) {
|
||||
if (map.hasOwnProperty(m)) {
|
||||
value = m;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof value === 'string') {
|
||||
value = value.toLowerCase().replace(/-/, "");
|
||||
if (map[value] !== null) {
|
||||
return map[value];
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief execute a server-side traversal
|
||||
///
|
||||
|
@ -683,7 +657,8 @@ function post_api_traversal(req, res) {
|
|||
catch (err2) {
|
||||
}
|
||||
|
||||
if (edgeCollection === undefined || edgeCollection == null) {
|
||||
if (edgeCollection === undefined ||
|
||||
edgeCollection === null) {
|
||||
return notFound(req, res, arangodb.ERROR_ARANGO_COLLECTION_NOT_FOUND, "invalid edgeCollection");
|
||||
}
|
||||
|
||||
|
@ -734,11 +709,7 @@ function post_api_traversal(req, res) {
|
|||
var expander;
|
||||
|
||||
if (json.direction !== undefined) {
|
||||
expander = validateArg(json.direction, {
|
||||
'outbound': traversal.outboundExpander,
|
||||
'inbound': traversal.inboundExpander,
|
||||
'any': traversal.anyExpander
|
||||
});
|
||||
expander = json.direction;
|
||||
}
|
||||
else if (json.expander !== undefined) {
|
||||
try {
|
||||
|
@ -761,35 +732,16 @@ function post_api_traversal(req, res) {
|
|||
params: json,
|
||||
edgeCollection: edgeCollection,
|
||||
datasource: traversal.collectionDatasourceFactory(edgeCollection),
|
||||
strategy: validateArg(json.strategy, {
|
||||
'depthfirst': Traverser.DEPTH_FIRST,
|
||||
'breadthfirst': Traverser.BREADTH_FIRST
|
||||
}),
|
||||
order: validateArg(json.order, {
|
||||
'preorder': Traverser.PRE_ORDER,
|
||||
'postorder': Traverser.POST_ORDER
|
||||
}),
|
||||
itemOrder: validateArg(json.itemOrder, {
|
||||
'forward': Traverser.FORWARD,
|
||||
'backward': Traverser.BACKWARD
|
||||
}),
|
||||
strategy: json.strategy,
|
||||
order: json.order,
|
||||
itemOrder: json.itemOrder,
|
||||
expander: expander,
|
||||
visitor: visitor,
|
||||
filter: filters,
|
||||
minDepth: json.minDepth || 0,
|
||||
minDepth: json.minDepth,
|
||||
maxDepth: json.maxDepth,
|
||||
uniqueness: {
|
||||
vertices: validateArg(json.uniqueness && json.uniqueness.vertices, {
|
||||
'global': Traverser.UNIQUE_GLOBAL,
|
||||
'none': Traverser.UNIQUE_NONE,
|
||||
'path': Traverser.UNIQUE_PATH
|
||||
}),
|
||||
edges: validateArg(json.uniqueness && json.uniqueness.edges, {
|
||||
'global': Traverser.UNIQUE_GLOBAL,
|
||||
'none': Traverser.UNIQUE_NONE,
|
||||
'path': Traverser.UNIQUE_PATH
|
||||
})
|
||||
}
|
||||
maxIterations: json.maxIterations,
|
||||
uniqueness: json.uniqueness
|
||||
};
|
||||
|
||||
// assemble result object
|
||||
|
@ -815,12 +767,17 @@ function post_api_traversal(req, res) {
|
|||
// run the traversal
|
||||
// -----------------------------------------
|
||||
|
||||
var traverser = new Traverser(config);
|
||||
var traverser;
|
||||
try {
|
||||
traverser = new Traverser(config);
|
||||
traverser.traverse(result, doc);
|
||||
actions.resultOk(req, res, actions.HTTP_OK, { result : result });
|
||||
}
|
||||
catch (err7) {
|
||||
if (traverser === undefined) {
|
||||
// error during traversal setup
|
||||
return badParam(req, res, err7);
|
||||
}
|
||||
actions.resultException(req, res, err7, undefined, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -144,6 +144,7 @@
|
|||
"ERROR_GRAPH_INVALID_EDGE" : { "code" : 1906, "message" : "invalid edge" },
|
||||
"ERROR_GRAPH_COULD_NOT_CREATE_EDGE" : { "code" : 1907, "message" : "could not create edge" },
|
||||
"ERROR_GRAPH_COULD_NOT_CHANGE_EDGE" : { "code" : 1908, "message" : "could not change edge" },
|
||||
"ERROR_GRAPH_TOO_MANY_ITERATIONS" : { "code" : 1909, "message" : "too many iterations" },
|
||||
"ERROR_SESSION_INVALID_SESSION" : { "code" : 1951, "message" : "invalid session" },
|
||||
"ERROR_SESSION_COULD_NOT_CREATE_SESSION" : { "code" : 1952, "message" : "could not create session" },
|
||||
"ERROR_SESSION_COULD_NOT_CHANGE_SESSION" : { "code" : 1953, "message" : "could not change session" },
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
var graph = require("org/arangodb/graph");
|
||||
var arangodb = require("org/arangodb");
|
||||
var ArangoError = arangodb.ArangoError;
|
||||
|
||||
var db = arangodb.db;
|
||||
|
||||
|
@ -660,6 +661,7 @@ function breadthFirstSearch () {
|
|||
},
|
||||
|
||||
run: function (config, result, startVertex) {
|
||||
var maxIterations = config.maxIterations, visitCounter = 0;
|
||||
var toVisit = [ { edge: null, vertex: startVertex, parentIndex: -1 } ];
|
||||
var visited = { edges: { }, vertices: { } };
|
||||
|
||||
|
@ -673,6 +675,13 @@ function breadthFirstSearch () {
|
|||
var vertex = current.vertex;
|
||||
var edge = current.edge;
|
||||
var path;
|
||||
|
||||
if (visitCounter++ > maxIterations) {
|
||||
var err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_GRAPH_TOO_MANY_ITERATIONS.code;
|
||||
err.errorMessage = arangodb.errors.ERROR_GRAPH_TOO_MANY_ITERATIONS.message;
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (current.visit === null || current.visit === undefined) {
|
||||
current.visit = false;
|
||||
|
@ -756,12 +765,20 @@ function depthFirstSearch () {
|
|||
},
|
||||
|
||||
run: function (config, result, startVertex) {
|
||||
var maxIterations = config.maxIterations, visitCounter = 0;
|
||||
var toVisit = [ { edge: null, vertex: startVertex, visit: null } ];
|
||||
var path = { edges: [ ], vertices: [ ] };
|
||||
var visited = { edges: { }, vertices: { } };
|
||||
var reverse = checkReverse(config);
|
||||
|
||||
while (toVisit.length > 0) {
|
||||
if (visitCounter++ > maxIterations) {
|
||||
var err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_GRAPH_TOO_MANY_ITERATIONS.code;
|
||||
err.errorMessage = arangodb.errors.ERROR_GRAPH_TOO_MANY_ITERATIONS.message;
|
||||
throw err;
|
||||
}
|
||||
|
||||
// peek at the top of the stack
|
||||
var current = toVisit[toVisit.length - 1];
|
||||
var vertex = current.vertex;
|
||||
|
@ -870,52 +887,150 @@ ArangoTraverser = function (config) {
|
|||
visitor: trackingVisitor,
|
||||
filter: visitAllFilter,
|
||||
expander: outboundExpander,
|
||||
datasource: null
|
||||
datasource: null,
|
||||
maxIterations: 10000,
|
||||
minDepth: 0,
|
||||
maxDepth: 256
|
||||
}, d;
|
||||
|
||||
var err;
|
||||
|
||||
if (typeof config !== "object") {
|
||||
throw "invalid configuration";
|
||||
err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_BAD_PARAMETER.code;
|
||||
err.errorMessage = arangodb.errors.ERROR_BAD_PARAMETER.message;
|
||||
throw err;
|
||||
}
|
||||
|
||||
// apply defaults
|
||||
for (d in defaults) {
|
||||
if (defaults.hasOwnProperty(d)) {
|
||||
if (! config.hasOwnProperty(d)) {
|
||||
if (! config.hasOwnProperty(d) || config[d] === undefined) {
|
||||
config[d] = defaults[d];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof config.visitor !== "function") {
|
||||
throw "invalid visitor";
|
||||
}
|
||||
|
||||
if (Array.isArray(config.filter)) {
|
||||
config.filter.forEach( function (f) {
|
||||
if (typeof f !== "function") {
|
||||
throw "invalid filter";
|
||||
function validate (value, map, param) {
|
||||
var m;
|
||||
|
||||
if (value === null || value === undefined) {
|
||||
// use first key from map
|
||||
for (m in map) {
|
||||
if (map.hasOwnProperty(m)) {
|
||||
value = m;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
value = value.toLowerCase().replace(/-/, "");
|
||||
if (map[value] !== null) {
|
||||
return map[value];
|
||||
}
|
||||
}
|
||||
for (m in map) {
|
||||
if (map.hasOwnProperty(m)) {
|
||||
if (map[m] === value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var innerFilters = config.filter.slice();
|
||||
err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_BAD_PARAMETER.code;
|
||||
err.errorMessage = "invalid value for " + param;
|
||||
throw err;
|
||||
}
|
||||
|
||||
config.uniqueness = {
|
||||
vertices: validate(config.uniqueness && config.uniqueness.vertices, {
|
||||
global: ArangoTraverser.UNIQUE_GLOBAL,
|
||||
none: ArangoTraverser.UNIQUE_NONE,
|
||||
path: ArangoTraverser.UNIQUE_PATH
|
||||
}, "uniqueness.vertices"),
|
||||
edges: validate(config.uniqueness && config.uniqueness.edges, {
|
||||
global: ArangoTraverser.UNIQUE_GLOBAL,
|
||||
none: ArangoTraverser.UNIQUE_NONE,
|
||||
path: ArangoTraverser.UNIQUE_PATH
|
||||
}, "uniqueness.edges")
|
||||
};
|
||||
|
||||
config.strategy = validate(config.strategy, {
|
||||
depthfirst: ArangoTraverser.DEPTH_FIRST,
|
||||
breadthfirst: ArangoTraverser.BREADTH_FIRST
|
||||
}, "strategy");
|
||||
|
||||
var combinedFilter = function (config, vertex, path) {
|
||||
return combineFilters(innerFilters, config, vertex, path);
|
||||
};
|
||||
config.order = validate(config.order, {
|
||||
preorder: ArangoTraverser.PRE_ORDER,
|
||||
postorder: ArangoTraverser.POST_ORDER
|
||||
}, "order");
|
||||
|
||||
config.filter = combinedFilter;
|
||||
config.itemOrder = validate(config.itemOrder, {
|
||||
forward: ArangoTraverser.FORWARD,
|
||||
backward: ArangoTraverser.BACKWARD
|
||||
}, "itemOrder");
|
||||
|
||||
|
||||
if (typeof config.visitor !== "function") {
|
||||
err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_BAD_PARAMETER.code;
|
||||
err.errorMessage = "invalid visitor function";
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (typeof config.filter !== "function") {
|
||||
throw "invalid filter";
|
||||
// prepare an array of filters
|
||||
var filters = [ ];
|
||||
if (config.minDepth !== undefined && config.minDepth >= 0) {
|
||||
filters.push(minDepthFilter);
|
||||
}
|
||||
if (config.maxDepth !== undefined && config.maxDepth > 0) {
|
||||
filters.push(maxDepthFilter);
|
||||
}
|
||||
|
||||
if (! Array.isArray(config.filter)) {
|
||||
config.filter = [ config.filter ];
|
||||
}
|
||||
|
||||
config.filter.forEach( function (f) {
|
||||
if (typeof f !== "function") {
|
||||
err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_BAD_PARAMETER.code;
|
||||
err.errorMessage = "invalid filter function";
|
||||
throw err;
|
||||
}
|
||||
|
||||
filters.push(f);
|
||||
});
|
||||
|
||||
if (filters.length === 0) {
|
||||
filters.push(visitAllFilter);
|
||||
}
|
||||
|
||||
config.filter = function (config, vertex, path) {
|
||||
return combineFilters(filters, config, vertex, path);
|
||||
};
|
||||
|
||||
if (typeof config.expander !== "function") {
|
||||
config.expander = validate(config.expander, {
|
||||
outbound: outboundExpander,
|
||||
inbound: inboundExpander,
|
||||
any: anyExpander
|
||||
}, "expander");
|
||||
}
|
||||
|
||||
if (typeof config.expander !== "function") {
|
||||
throw "invalid expander";
|
||||
err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_BAD_PARAMETER.code;
|
||||
err.errorMessage = "invalid expander function";
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (typeof config.datasource !== "object") {
|
||||
throw "invalid datasource";
|
||||
err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_BAD_PARAMETER.code;
|
||||
err.errorMessage = "invalid datasource";
|
||||
throw err;
|
||||
}
|
||||
|
||||
this.config = config;
|
||||
|
|
|
@ -1853,8 +1853,14 @@ function resultException (req, res, err, headers, verbose) {
|
|||
switch (num) {
|
||||
case arangodb.ERROR_INTERNAL:
|
||||
case arangodb.ERROR_OUT_OF_MEMORY:
|
||||
case arangodb.ERROR_GRAPH_TOO_MANY_ITERATIONS:
|
||||
code = exports.HTTP_SERVER_ERROR;
|
||||
break;
|
||||
|
||||
case arangodb.ERROR_ARANGO_COLLECTION_NOT_FOUND:
|
||||
case arangodb.ERROR_ARANGO_DOCUMENT_NOT_FOUND:
|
||||
code = exports.HTTP_NOT_FOUND;
|
||||
break;
|
||||
|
||||
case arangodb.ERROR_ARANGO_DUPLICATE_NAME:
|
||||
case arangodb.ERROR_ARANGO_DUPLICATE_IDENTIFIER:
|
||||
|
|
|
@ -3478,27 +3478,6 @@ function TRAVERSAL_FUNC (func, vertexCollection, edgeCollection, startVertex, di
|
|||
});
|
||||
}
|
||||
|
||||
function validate (value, map) {
|
||||
if (value === null || value === undefined) {
|
||||
var m;
|
||||
// use first key from map
|
||||
for (m in map) {
|
||||
if (map.hasOwnProperty(m)) {
|
||||
value = m;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
value = value.toLowerCase().replace(/-/, "");
|
||||
if (map[value] !== null) {
|
||||
return map[value];
|
||||
}
|
||||
}
|
||||
|
||||
THROW(INTERNAL.errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH, func);
|
||||
}
|
||||
|
||||
if (typeof params.visitor !== "function") {
|
||||
THROW(INTERNAL.errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH, func);
|
||||
}
|
||||
|
@ -3508,55 +3487,16 @@ function TRAVERSAL_FUNC (func, vertexCollection, edgeCollection, startVertex, di
|
|||
params.maxDepth = 256;
|
||||
}
|
||||
|
||||
// prepare an array of filters
|
||||
var filters = [ ];
|
||||
if (params.minDepth !== undefined) {
|
||||
filters.push(TRAVERSAL.minDepthFilter);
|
||||
}
|
||||
if (params.maxDepth !== undefined) {
|
||||
filters.push(TRAVERSAL.maxDepthFilter);
|
||||
}
|
||||
if (filters.length === 0) {
|
||||
filters.push(TRAVERSAL.visitAllFilter);
|
||||
}
|
||||
|
||||
var config = {
|
||||
connect: params.connect,
|
||||
datasource: TRAVERSAL.collectionDatasourceFactory(edgeCollection),
|
||||
strategy: validate(params.strategy, {
|
||||
'depthfirst': TRAVERSAL.Traverser.DEPTH_FIRST,
|
||||
'breadthfirst': TRAVERSAL.Traverser.BREADTH_FIRST
|
||||
}),
|
||||
order: validate(params.order, {
|
||||
'preorder': TRAVERSAL.Traverser.PRE_ORDER,
|
||||
'postorder': TRAVERSAL.Traverser.POST_ORDER
|
||||
}),
|
||||
itemOrder: validate(params.itemOrder, {
|
||||
'forward': TRAVERSAL.Traverser.FORWARD,
|
||||
'backward': TRAVERSAL.Traverser.BACKWARD
|
||||
}),
|
||||
trackPaths: params.paths || false,
|
||||
visitor: params.visitor,
|
||||
maxDepth: params.maxDepth,
|
||||
minDepth: params.minDepth,
|
||||
filter: filters,
|
||||
uniqueness: {
|
||||
vertices: validate(params.uniqueness && params.uniqueness.vertices, {
|
||||
'global': TRAVERSAL.Traverser.UNIQUE_GLOBAL,
|
||||
'none': TRAVERSAL.Traverser.UNIQUE_NONE,
|
||||
'path': TRAVERSAL.Traverser.UNIQUE_PATH
|
||||
}),
|
||||
edges: validate(params.uniqueness && params.uniqueness.edges, {
|
||||
'global': TRAVERSAL.Traverser.UNIQUE_GLOBAL,
|
||||
'none': TRAVERSAL.Traverser.UNIQUE_NONE,
|
||||
'path': TRAVERSAL.Traverser.UNIQUE_PATH
|
||||
})
|
||||
},
|
||||
expander: validate(direction, {
|
||||
'outbound': TRAVERSAL.outboundExpander,
|
||||
'inbound': TRAVERSAL.inboundExpander,
|
||||
'any': TRAVERSAL.anyExpander
|
||||
})
|
||||
maxIterations: params.maxIterations,
|
||||
uniqueness: params.uniqueness,
|
||||
expander: direction
|
||||
};
|
||||
|
||||
if (params.followEdges) {
|
||||
|
|
|
@ -198,7 +198,7 @@ ERROR_KEYVALUE_KEY_NOT_REMOVED,1805,"key value not removed","Will be raised when
|
|||
ERROR_KEYVALUE_NO_VALUE,1806,"missing value","Will be raised when the value is missing"
|
||||
|
||||
################################################################################
|
||||
## Graph errors
|
||||
## Graph / traversal errors
|
||||
################################################################################
|
||||
|
||||
ERROR_GRAPH_INVALID_GRAPH,1901,"invalid graph","Will be raised when an invalid name is passed to the server"
|
||||
|
@ -209,6 +209,7 @@ ERROR_GRAPH_COULD_NOT_CHANGE_VERTEX,1905,"could not change vertex","Will be rais
|
|||
ERROR_GRAPH_INVALID_EDGE,1906,"invalid edge","Will be raised when an invalid edge id is passed to the server"
|
||||
ERROR_GRAPH_COULD_NOT_CREATE_EDGE,1907,"could not create edge","Will be raised when the edge could not be created"
|
||||
ERROR_GRAPH_COULD_NOT_CHANGE_EDGE,1908,"could not change edge","Will be raised when the edge could not be changed"
|
||||
ERROR_GRAPH_TOO_MANY_ITERATIONS,1909,"too many iterations","Will be raised when too many iterations are done in a graph traversal"
|
||||
|
||||
################################################################################
|
||||
## Session errors
|
||||
|
|
|
@ -140,6 +140,7 @@ void TRI_InitialiseErrorMessages (void) {
|
|||
REG_ERROR(ERROR_GRAPH_INVALID_EDGE, "invalid edge");
|
||||
REG_ERROR(ERROR_GRAPH_COULD_NOT_CREATE_EDGE, "could not create edge");
|
||||
REG_ERROR(ERROR_GRAPH_COULD_NOT_CHANGE_EDGE, "could not change edge");
|
||||
REG_ERROR(ERROR_GRAPH_TOO_MANY_ITERATIONS, "too many iterations");
|
||||
REG_ERROR(ERROR_SESSION_INVALID_SESSION, "invalid session");
|
||||
REG_ERROR(ERROR_SESSION_COULD_NOT_CREATE_SESSION, "could not create session");
|
||||
REG_ERROR(ERROR_SESSION_COULD_NOT_CHANGE_SESSION, "could not change session");
|
||||
|
|
|
@ -306,6 +306,8 @@ extern "C" {
|
|||
/// Will be raised when the edge could not be created
|
||||
/// - 1908: @LIT{could not change edge}
|
||||
/// Will be raised when the edge could not be changed
|
||||
/// - 1909: @LIT{too many iterations}
|
||||
/// Will be raised when too many iterations are done in a graph traversal
|
||||
/// - 1951: @LIT{invalid session}
|
||||
/// Will be raised when an invalid session id is passed to the server
|
||||
/// - 1952: @LIT{could not create session}
|
||||
|
@ -1751,6 +1753,16 @@ void TRI_InitialiseErrorMessages (void);
|
|||
|
||||
#define TRI_ERROR_GRAPH_COULD_NOT_CHANGE_EDGE (1908)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief 1909: ERROR_GRAPH_TOO_MANY_ITERATIONS
|
||||
///
|
||||
/// too many iterations
|
||||
///
|
||||
/// Will be raised when too many iterations are done in a graph traversal
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define TRI_ERROR_GRAPH_TOO_MANY_ITERATIONS (1909)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief 1951: ERROR_SESSION_INVALID_SESSION
|
||||
///
|
||||
|
|
Loading…
Reference in New Issue