mirror of https://gitee.com/bigwinds/arangodb
Merge branch 'devel' of github.com:triAGENS/ArangoDB into devel
This commit is contained in:
commit
dbcdd72da4
44
CHANGELOG
44
CHANGELOG
|
@ -1,6 +1,50 @@
|
||||||
v1.5.0 (XXXX-XX-XX)
|
v1.5.0 (XXXX-XX-XX)
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
* allow vertex and edge filtering with user-defined functions in TRAVERSAL,
|
||||||
|
TRAVERSAL_TREE and SHORTEST_PATH AQL functions:
|
||||||
|
|
||||||
|
// using user-defined AQL functions for edge and vertex filtering
|
||||||
|
RETURN TRAVERSAL(friends, friendrelations, "friends/john", "outbound", {
|
||||||
|
followEdges: "myfunctions::checkedge",
|
||||||
|
filterVertices: "myfunctions::checkvertex"
|
||||||
|
})
|
||||||
|
|
||||||
|
// using the following custom filter functions
|
||||||
|
var aqlfunctions = require("org/arangodb/aql/functions");
|
||||||
|
aqlfunctions.register("myfunctions::checkedge", function (config, vertex, edge, path) {
|
||||||
|
return (edge.type !== 'dislikes'); // don't follow these edges
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
aqlfunctions.register("myfunctions::checkvertex", function (config, vertex, path) {
|
||||||
|
if (vertex.isDeleted || ! vertex.isActive) {
|
||||||
|
return [ "prune", "exclude" ]; // exclude these and don't follow them
|
||||||
|
}
|
||||||
|
return [ ]; // include everything else
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
|
||||||
|
* added SHORTEST_PATH AQL function
|
||||||
|
|
||||||
|
this calculates the shortest paths between two vertices, using the Dijkstra
|
||||||
|
algorithm, employing a min-heap
|
||||||
|
|
||||||
|
By default, ArangoDB does not know the distance between any two vertices and
|
||||||
|
will use a default distance of 1. A custom distance function can be registered
|
||||||
|
as an AQL user function to make the distance calculation use any document
|
||||||
|
attributes or custom logic:
|
||||||
|
|
||||||
|
RETURN SHORTEST_PATH(cities, motorways, "cities/CGN", "cities/MUC", "outbound", {
|
||||||
|
paths: true,
|
||||||
|
distance: "myfunctions::citydistance"
|
||||||
|
})
|
||||||
|
|
||||||
|
// using the following custom distance function
|
||||||
|
var aqlfunctions = require("org/arangodb/aql/functions");
|
||||||
|
aqlfunctions.register("myfunctions::distance", function (config, vertex1, vertex2, edge) {
|
||||||
|
return Math.sqrt(Math.pow(vertex1.x - vertex2.x) + Math.pow(vertex1.y - vertex2.y));
|
||||||
|
}, false);
|
||||||
|
|
||||||
* issue #751: Create database through API should return HTTP status code 201
|
* issue #751: Create database through API should return HTTP status code 201
|
||||||
|
|
||||||
By default, the server now returns HTTP 201 (created) when creating a new
|
By default, the server now returns HTTP 201 (created) when creating a new
|
||||||
|
|
|
@ -1420,7 +1420,7 @@ Example calls:
|
||||||
- `paths`: if `true`, the paths encountered during the traversal will
|
- `paths`: if `true`, the paths encountered during the traversal will
|
||||||
also be returned along with each traversed vertex. If `false`, only the
|
also be returned along with each traversed vertex. If `false`, only the
|
||||||
encountered vertices will be returned.
|
encountered vertices will be returned.
|
||||||
- `uniqueness`: an optional document with the following properties:
|
- `uniqueness`: an optional document with the following attributes:
|
||||||
- `vertices`:
|
- `vertices`:
|
||||||
- `none`: no vertex uniqueness is enforced
|
- `none`: no vertex uniqueness is enforced
|
||||||
- `global`: a vertex may be visited at most once. This is the default.
|
- `global`: a vertex may be visited at most once. This is the default.
|
||||||
|
@ -1434,22 +1434,44 @@ Example calls:
|
||||||
- `followEdges`: an optional list of example edge documents that the traversal will
|
- `followEdges`: an optional list of example edge documents that the traversal will
|
||||||
expand into. If no examples are given, the traversal will follow all edges. If one
|
expand into. If no examples are given, the traversal will follow all edges. If one
|
||||||
or many edge examples are given, the traversal will only follow an edge if it matches
|
or many edge examples are given, the traversal will only follow an edge if it matches
|
||||||
at least one of the specified examples.
|
at least one of the specified examples. `followEdges` can also be a string with the
|
||||||
|
name of an AQL user-defined function that should be responsible for checking if an
|
||||||
|
edge should be followed. In this case, the AQL function will is expected to have the
|
||||||
|
following signature:
|
||||||
|
|
||||||
|
function (config, vertex, edge, path)
|
||||||
|
|
||||||
|
The function is expected to return a boolean value. If ìt returns `true`, the edge
|
||||||
|
will be followed. If `false` is returned, the edge will be ignored.
|
||||||
|
|
||||||
- `filterVertices`: an optional list of example vertex documents that the traversal will
|
- `filterVertices`: an optional list of example vertex documents that the traversal will
|
||||||
treat specially. If no examples are given, the traversal will handle all encountered
|
treat specially. If no examples are given, the traversal will handle all encountered
|
||||||
vertices equally. If one or many vertex examples are given, the traversal will exclude
|
vertices equally. If one or many vertex examples are given, the traversal will exclude
|
||||||
the vertex from the result and/or descend into it.
|
the vertex from the result and/or not descend into it. Optionally, `filterVertices` can
|
||||||
- `vertexFilterMethod`: only useful in conjunction with `filterVertices`. If specified,
|
contain the name of a user-defined AQL function that should be responsible for filtering.
|
||||||
it will influence how vertices are handled that don't match the examples in `filterVertices`:
|
If so, the AQL function is expected to have the following signature:
|
||||||
|
|
||||||
|
function (config, vertex, path)
|
||||||
|
|
||||||
|
If a custom AQL function is used, it is expected to return one of the following values:
|
||||||
|
- `[ ]`: include the vertex in the result and descend into its connected edges
|
||||||
|
- `[ "prune" ]`: will include the vertex in the result but not descend into its connected edges
|
||||||
|
- `[ "exclude" ]`: will not include the vertex in the result but descend into its connected edges
|
||||||
|
- `[ "prune", "exclude" ]`: will completely ignore the vertex and its connected edges
|
||||||
|
|
||||||
|
- `vertexFilterMethod`: only useful in conjunction with `filterVertices` and if no user-defined
|
||||||
|
AQL function is used.. If specified, it will influence how vertices are handled that don't match
|
||||||
|
the examples in `filterVertices`:
|
||||||
- `[ "prune" ]`: will include non-matching vertices in the result but not descend into them
|
- `[ "prune" ]`: will include non-matching vertices in the result but not descend into them
|
||||||
- `[ "exclude" ]`: will not include non-matching vertices in the result but descend into them
|
- `[ "exclude" ]`: will not include non-matching vertices in the result but descend into them
|
||||||
- `[ "prune", "exclude" ]`: will neither include non-matching vertices in the result nor descend into them
|
- `[ "prune", "exclude" ]`: will neither include non-matching vertices in the result nor descend into them
|
||||||
|
|
||||||
The result of the TRAVERSAL function is a list of traversed points. Each point is a
|
The result of the TRAVERSAL function is a list of traversed points. Each point is a
|
||||||
document consisting of the following properties:
|
document consisting of the following attributes:
|
||||||
- `vertex`: the vertex at the traversal point
|
- `vertex`: the vertex at the traversal point
|
||||||
- `path`: The path history for the traversal point. The path is a document with the
|
- `path`: The path history for the traversal point. The path is a document with the
|
||||||
properties `vertices` and `edges`, which are both lists.
|
attributes `vertices` and `edges`, which are both lists. Note that `path` is only present
|
||||||
|
in the result if the `paths` attribute is set in the @FA{options}.
|
||||||
|
|
||||||
Example calls:
|
Example calls:
|
||||||
|
|
||||||
|
@ -1458,9 +1480,10 @@ Example calls:
|
||||||
order: "postorder",
|
order: "postorder",
|
||||||
itemOrder: "backward",
|
itemOrder: "backward",
|
||||||
maxDepth: 6,
|
maxDepth: 6,
|
||||||
trackPaths: true
|
paths: true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// filtering on specific edges (by specifying example edges)
|
||||||
TRAVERSAL(friends, friendrelations, "friends/john", "outbound", {
|
TRAVERSAL(friends, friendrelations, "friends/john", "outbound", {
|
||||||
strategy: "breadthfirst",
|
strategy: "breadthfirst",
|
||||||
order: "preorder",
|
order: "preorder",
|
||||||
|
@ -1468,15 +1491,38 @@ Example calls:
|
||||||
followEdges: [ { type: "knows" }, { state: "FL" } ]
|
followEdges: [ { type: "knows" }, { state: "FL" } ]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// filtering on specific edges and vertices
|
||||||
TRAVERSAL(friends, friendrelations, "friends/john", "outbound", {
|
TRAVERSAL(friends, friendrelations, "friends/john", "outbound", {
|
||||||
strategy: "breadthfirst",
|
strategy: "breadthfirst",
|
||||||
order: "preorder",
|
order: "preorder",
|
||||||
itemOrder: "forward",
|
itemOrder: "forward",
|
||||||
followEdges: [ { type: "knows" }, { state: "FL" } ],
|
followEdges: [ { type: "knows" }, { state: "FL" } ],
|
||||||
filterVertices: [ { isActive: true } ],
|
filterVertices: [ { isActive: true }, { isDeleted: false } ],
|
||||||
vertexFilterMethod: [ "prune", "exclude" ]
|
vertexFilterMethod: [ "prune", "exclude" ]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// using user-defined AQL functions for edge and vertex filtering
|
||||||
|
TRAVERSAL(friends, friendrelations, "friends/john", "outbound", {
|
||||||
|
followEdges: "myfunctions::checkedge",
|
||||||
|
filterVertices: "myfunctions::checkvertex"
|
||||||
|
})
|
||||||
|
|
||||||
|
// to register the custom AQL functions, execute something in the fashion of the
|
||||||
|
// following commands in arangosh once:
|
||||||
|
var aqlfunctions = require("org/arangodb/aql/functions");
|
||||||
|
|
||||||
|
// these are the actual filter functions
|
||||||
|
aqlfunctions.register("myfunctions::checkedge", function (config, vertex, edge, path) {
|
||||||
|
return (edge.type !== 'dislikes'); // don't follow these edges
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
aqlfunctions.register("myfunctions::checkvertex", function (config, vertex, path) {
|
||||||
|
if (vertex.isDeleted || ! vertex.isActive) {
|
||||||
|
return [ "prune", "exclude" ]; // exclude these and don't follow them
|
||||||
|
}
|
||||||
|
return [ ]; // include everything else
|
||||||
|
}, false);
|
||||||
|
|
||||||
- @FN{TRAVERSAL_TREE(@FA{vertexcollection}, @FA{edgecollection}, @FA{startVertex}, @FA{direction}, @FA{connectName}, @FA{options})}:
|
- @FN{TRAVERSAL_TREE(@FA{vertexcollection}, @FA{edgecollection}, @FA{startVertex}, @FA{direction}, @FA{connectName}, @FA{options})}:
|
||||||
traverses the graph described by @FA{vertexcollection} and @FA{edgecollection},
|
traverses the graph described by @FA{vertexcollection} and @FA{edgecollection},
|
||||||
starting at the vertex identified by id @FA{startVertex} and creates a hierchical result.
|
starting at the vertex identified by id @FA{startVertex} and creates a hierchical result.
|
||||||
|
@ -1490,7 +1536,7 @@ Example calls:
|
||||||
|
|
||||||
Example calls:
|
Example calls:
|
||||||
|
|
||||||
TREE(friends, friendrelations, "friends/john", "outbound", "likes", {
|
TRAVERSAL_TREE(friends, friendrelations, "friends/john", "outbound", "likes", {
|
||||||
itemOrder: "forward"
|
itemOrder: "forward"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1501,6 +1547,106 @@ If no bounds are set, a traversal might run into an endless loop in a cyclic gra
|
||||||
and even in a non-cyclic graph, traversing far into the graph might consume a lot of processing
|
and even in a non-cyclic graph, traversing far into the graph might consume a lot of processing
|
||||||
time and memory for the result set.
|
time and memory for the result set.
|
||||||
|
|
||||||
|
- @FN{SHORTEST_PATH(@FA{vertexcollection}, @FA{edgecollection}, @FA{startVertex}, @FA{endVertex}, @FA{direction}, @FA{options})}:
|
||||||
|
determines the first shortest path from the @FA{startVertex} to the @FA{endVertex}.
|
||||||
|
Both vertices must be present in the vertex collection specified in @FA{vertexcollection},
|
||||||
|
and any connecting edges must be present in the collection specified by @FA{edgecollection}.
|
||||||
|
Vertex connectivity is specified by the @FA{direction} parameter:
|
||||||
|
- `"outbound"`: vertices are connected in `_from` to `_to` order
|
||||||
|
- `"inbound"`: vertices are connected in `_to` to `_from` order
|
||||||
|
- `"any"`: vertices are connected in both `_to` to `_from` and in
|
||||||
|
`_from` to `_to` order
|
||||||
|
The search is aborted when a shortest path is found. Only the first shortest path will be
|
||||||
|
returned. Any vertex will be visited at most once by the search.
|
||||||
|
|
||||||
|
Additional options for the traversal can be provided via the @FA{options} document:
|
||||||
|
- `maxIterations`: Maximum number of iterations in the search. This number can be
|
||||||
|
set to bound long-running searches. When a search performs as many iterations as the
|
||||||
|
`maxIterations` value, the search will abort with an error. If `maxIterations` is not
|
||||||
|
set, a server-defined value may be used.
|
||||||
|
- `paths`: if `true`, the result will not only contain the vertices along the shortest
|
||||||
|
path, but also the connecting edges. If `false`, only the encountered vertices will
|
||||||
|
be returned.
|
||||||
|
- `distance`: an optional custom function to be used when calculating the distance
|
||||||
|
between a vertex and a neighboring vertex. The expected function signature is:
|
||||||
|
|
||||||
|
function (config, vertex1, vertex2, edge)
|
||||||
|
|
||||||
|
Both vertices and the connecting edge will be passed into the function. The function
|
||||||
|
is expected to return a numeric value that expresses the distance between the two
|
||||||
|
vertices. Higher values will mean higher distances, giving the connection a lower
|
||||||
|
priority in further analysis.
|
||||||
|
If no custom distance function is specified, all vertices are assumed to have the
|
||||||
|
same distance (1) to each other. If a function name is specified, it must have been
|
||||||
|
registered as a regular user-defined AQL function.
|
||||||
|
|
||||||
|
- `followEdges`: an optional list of example edge documents that the search will
|
||||||
|
expand into. If no examples are given, the search will follow all edges. If one
|
||||||
|
or many edge examples are given, the search will only follow an edge if it matches
|
||||||
|
at least one of the specified examples. `followEdges` can also be a string with the
|
||||||
|
name of an AQL user-defined function that should be responsible for checking if an
|
||||||
|
edge should be followed. In this case, the AQL function will is expected to have the
|
||||||
|
following signature:
|
||||||
|
|
||||||
|
function (config, vertex, edge, path)
|
||||||
|
|
||||||
|
The function is expected to return a boolean value. If ìt returns `true`, the edge
|
||||||
|
will be followed. If `false` is returned, the edge will be ignored.
|
||||||
|
|
||||||
|
- `filterVertices`: an optional list of example vertex documents that the search will
|
||||||
|
treat specially. If no examples are given, the search will handle all encountered
|
||||||
|
vertices equally. If one or many vertex examples are given, the search will exclude
|
||||||
|
the vertex from the result and/or not descend into it. Optionally, `filterVertices` can
|
||||||
|
contain the name of a user-defined AQL function that should be responsible for filtering.
|
||||||
|
If so, the AQL function is expected to have the following signature:
|
||||||
|
|
||||||
|
function (config, vertex, path)
|
||||||
|
|
||||||
|
If a custom AQL function is used, it is expected to return one of the following values:
|
||||||
|
- `[ ]`: include the vertex in the result and descend into its connected edges
|
||||||
|
- `[ "prune" ]`: will include the vertex in the result but not descend into its connected edges
|
||||||
|
- `[ "exclude" ]`: will not include the vertex in the result but descend into its connected edges
|
||||||
|
- `[ "prune", "exclude" ]`: will completely ignore the vertex and its connected edges
|
||||||
|
|
||||||
|
The result of the SHORTEST_PATH function is a list with the components of the shortest
|
||||||
|
path. Each component is a document consisting of the following attributes:
|
||||||
|
- `vertex`: the vertex at the traversal point
|
||||||
|
- `path`: The path history for the traversal point. The path is a document with the
|
||||||
|
attributes `vertices` and `edges`, which are both lists. Note that `path` is only present
|
||||||
|
in the result if the `paths` attribute is set in the @FA{options}.
|
||||||
|
|
||||||
|
Example calls:
|
||||||
|
|
||||||
|
SHORTEST_PATH(cities, motorways, "cities/CGN", "cities/MUC", "outbound", {
|
||||||
|
paths: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// using a user-defined distance function
|
||||||
|
SHORTEST_PATH(cities, motorways, "cities/CGN", "cities/MUC", "outbound", {
|
||||||
|
paths: true,
|
||||||
|
distance: "myfunctions::citydistance"
|
||||||
|
})
|
||||||
|
|
||||||
|
// using a user-defined function to filter edges
|
||||||
|
SHORTEST_PATH(cities, motorways, "cities/CGN", "cities/MUC", "outbound", {
|
||||||
|
paths: true,
|
||||||
|
followEdges: "myfunctions::checkedge"
|
||||||
|
})
|
||||||
|
|
||||||
|
// to register a custom AQL distance function, execute something in the fashion of the
|
||||||
|
// following commands in arangosh once:
|
||||||
|
var aqlfunctions = require("org/arangodb/aql/functions");
|
||||||
|
|
||||||
|
// this is the actual distance function
|
||||||
|
aqlfunctions.register("myfunctions::distance", function (config, vertex1, vertex2, edge) {
|
||||||
|
return Math.sqrt(Math.pow(vertex1.x - vertex2.x) + Math.pow(vertex1.y - vertex2.y));
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
// this is the filter function for the edges
|
||||||
|
aqlfunctions.register("myfunctions::checkedge", function (config, vertex, edge, path) {
|
||||||
|
return (edge.underConstruction === false); // don't follow these edges
|
||||||
|
}, false);
|
||||||
|
|
||||||
- @FN{EDGES(@FA{edgecollection}, @FA{startvertex}, @FA{direction}, @FA{edgeexamples})}:
|
- @FN{EDGES(@FA{edgecollection}, @FA{startvertex}, @FA{direction}, @FA{edgeexamples})}:
|
||||||
return all edges connected to the vertex @FA{startvertex} as a list. The possible values for
|
return all edges connected to the vertex @FA{startvertex} as a list. The possible values for
|
||||||
@FA{direction} are:
|
@FA{direction} are:
|
||||||
|
|
|
@ -701,8 +701,9 @@ TRI_associative_pointer_t* TRI_CreateFunctionsAql (void) {
|
||||||
|
|
||||||
// graph functions
|
// graph functions
|
||||||
REGISTER_FUNCTION("PATHS", "GRAPH_PATHS", false, false, "c,h|s,b", &OptimisePaths);
|
REGISTER_FUNCTION("PATHS", "GRAPH_PATHS", false, false, "c,h|s,b", &OptimisePaths);
|
||||||
REGISTER_FUNCTION("TRAVERSAL", "GRAPH_TRAVERSAL", false, false, "h,h,s,s,a", NULL);
|
REGISTER_FUNCTION("SHORTEST_PATH", "GRAPH_SHORTEST_PATH", false, false, "h,h,s,s,s|a", NULL);
|
||||||
REGISTER_FUNCTION("TRAVERSAL_TREE", "GRAPH_TRAVERSAL_TREE", false, false, "h,h,s,s,s,a", NULL);
|
REGISTER_FUNCTION("TRAVERSAL", "GRAPH_TRAVERSAL", false, false, "h,h,s,s|a", NULL);
|
||||||
|
REGISTER_FUNCTION("TRAVERSAL_TREE", "GRAPH_TRAVERSAL_TREE", false, false, "h,h,s,s,s|a", NULL);
|
||||||
REGISTER_FUNCTION("EDGES", "GRAPH_EDGES", false, false, "h,s,s|l", NULL);
|
REGISTER_FUNCTION("EDGES", "GRAPH_EDGES", false, false, "h,s,s|l", NULL);
|
||||||
REGISTER_FUNCTION("NEIGHBORS", "GRAPH_NEIGHBORS", false, false, "h,h,s,s|l", NULL);
|
REGISTER_FUNCTION("NEIGHBORS", "GRAPH_NEIGHBORS", false, false, "h,h,s,s|l", NULL);
|
||||||
|
|
||||||
|
|
|
@ -127,6 +127,10 @@ var actions = require("org/arangodb/actions");
|
||||||
/// If the transaction specification is either missing or malformed, the server
|
/// If the transaction specification is either missing or malformed, the server
|
||||||
/// will respond with `HTTP 400`.
|
/// will respond with `HTTP 400`.
|
||||||
///
|
///
|
||||||
|
/// @RESTRETURNCODE{404}
|
||||||
|
/// If the transaction specification contains an unknown collection, the server
|
||||||
|
/// will respond with `HTTP 404`.
|
||||||
|
///
|
||||||
/// @RESTRETURNCODE{500}
|
/// @RESTRETURNCODE{500}
|
||||||
/// Exceptions thrown by users will make the server respond with a return code of
|
/// Exceptions thrown by users will make the server respond with a return code of
|
||||||
/// `HTTP 500`
|
/// `HTTP 500`
|
||||||
|
|
|
@ -105,8 +105,13 @@ abortedException.prototype = new Error();
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
function collectionDatasourceFactory (edgeCollection) {
|
function collectionDatasourceFactory (edgeCollection) {
|
||||||
|
var c = edgeCollection;
|
||||||
|
if (typeof c === 'string') {
|
||||||
|
c = db._collection(c);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
edgeCollection: edgeCollection,
|
edgeCollection: c,
|
||||||
|
|
||||||
getVertexId: function (vertex) {
|
getVertexId: function (vertex) {
|
||||||
return vertex._id;
|
return vertex._id;
|
||||||
|
@ -947,14 +952,13 @@ function dijkstraSearch () {
|
||||||
var i, n;
|
var i, n;
|
||||||
|
|
||||||
if (currentNode.vertex._id === endVertex._id) {
|
if (currentNode.vertex._id === endVertex._id) {
|
||||||
var vertices = this.vertexList(currentNode);
|
var vertices = this.vertexList(currentNode).reverse();
|
||||||
if (config.order !== ArangoTraverser.PRE_ORDER) {
|
|
||||||
vertices.reverse();
|
|
||||||
}
|
|
||||||
|
|
||||||
n = vertices.length;
|
n = vertices.length;
|
||||||
for (i = 0; i < n; ++i) {
|
for (i = 0; i < n; ++i) {
|
||||||
config.visitor(config, result, vertices[i].vertex, this.buildPath(vertices[i]));
|
if (! vertices[i].hide) {
|
||||||
|
config.visitor(config, result, vertices[i].vertex, this.buildPath(vertices[i]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -968,9 +972,19 @@ function dijkstraSearch () {
|
||||||
}
|
}
|
||||||
|
|
||||||
currentNode.visited = true;
|
currentNode.visited = true;
|
||||||
var dist = currentNode.dist;
|
|
||||||
|
|
||||||
var path = this.buildPath(currentNode);
|
var path = this.buildPath(currentNode);
|
||||||
|
var filterResult = parseFilterResult(config.filter(config, currentNode.vertex, path));
|
||||||
|
|
||||||
|
if (! filterResult.visit) {
|
||||||
|
currentNode.hide = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! filterResult.expand) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dist = currentNode.dist;
|
||||||
var connected = config.expander(config, currentNode.vertex, path);
|
var connected = config.expander(config, currentNode.vertex, path);
|
||||||
n = connected.length;
|
n = connected.length;
|
||||||
|
|
||||||
|
|
|
@ -104,8 +104,13 @@ abortedException.prototype = new Error();
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
function collectionDatasourceFactory (edgeCollection) {
|
function collectionDatasourceFactory (edgeCollection) {
|
||||||
|
var c = edgeCollection;
|
||||||
|
if (typeof c === 'string') {
|
||||||
|
c = db._collection(c);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
edgeCollection: edgeCollection,
|
edgeCollection: c,
|
||||||
|
|
||||||
getVertexId: function (vertex) {
|
getVertexId: function (vertex) {
|
||||||
return vertex._id;
|
return vertex._id;
|
||||||
|
@ -946,14 +951,13 @@ function dijkstraSearch () {
|
||||||
var i, n;
|
var i, n;
|
||||||
|
|
||||||
if (currentNode.vertex._id === endVertex._id) {
|
if (currentNode.vertex._id === endVertex._id) {
|
||||||
var vertices = this.vertexList(currentNode);
|
var vertices = this.vertexList(currentNode).reverse();
|
||||||
if (config.order !== ArangoTraverser.PRE_ORDER) {
|
|
||||||
vertices.reverse();
|
|
||||||
}
|
|
||||||
|
|
||||||
n = vertices.length;
|
n = vertices.length;
|
||||||
for (i = 0; i < n; ++i) {
|
for (i = 0; i < n; ++i) {
|
||||||
config.visitor(config, result, vertices[i].vertex, this.buildPath(vertices[i]));
|
if (! vertices[i].hide) {
|
||||||
|
config.visitor(config, result, vertices[i].vertex, this.buildPath(vertices[i]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -967,9 +971,19 @@ function dijkstraSearch () {
|
||||||
}
|
}
|
||||||
|
|
||||||
currentNode.visited = true;
|
currentNode.visited = true;
|
||||||
var dist = currentNode.dist;
|
|
||||||
|
|
||||||
var path = this.buildPath(currentNode);
|
var path = this.buildPath(currentNode);
|
||||||
|
var filterResult = parseFilterResult(config.filter(config, currentNode.vertex, path));
|
||||||
|
|
||||||
|
if (! filterResult.visit) {
|
||||||
|
currentNode.hide = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! filterResult.expand) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dist = currentNode.dist;
|
||||||
var connected = config.expander(config, currentNode.vertex, path);
|
var connected = config.expander(config, currentNode.vertex, path);
|
||||||
n = connected.length;
|
n = connected.length;
|
||||||
|
|
||||||
|
|
|
@ -3751,7 +3751,7 @@ function TRAVERSAL_TREE_VISITOR (config, result, vertex, path) {
|
||||||
/// @brief expander callback function for traversal
|
/// @brief expander callback function for traversal
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
function TRAVERSAL_FILTER (config, vertex, edge, path) {
|
function TRAVERSAL_EDGE_EXAMPLE_FILTER (config, vertex, edge, path) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
return MATCHES(edge, config.expandEdgeExamples);
|
return MATCHES(edge, config.expandEdgeExamples);
|
||||||
|
@ -3764,7 +3764,7 @@ function TRAVERSAL_FILTER (config, vertex, edge, path) {
|
||||||
function TRAVERSAL_VERTEX_FILTER (config, vertex, path) {
|
function TRAVERSAL_VERTEX_FILTER (config, vertex, path) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
if (!MATCHES(vertex, config.filterVertexExamples)) {
|
if (! MATCHES(vertex, config.filterVertexExamples)) {
|
||||||
return config.vertexFilterMethod;
|
return config.vertexFilterMethod;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3775,6 +3775,11 @@ function TRAVERSAL_VERTEX_FILTER (config, vertex, path) {
|
||||||
|
|
||||||
function TRAVERSAL_CHECK_EXAMPLES_TYPEWEIGHTS (examples, func) {
|
function TRAVERSAL_CHECK_EXAMPLES_TYPEWEIGHTS (examples, func) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
if (TYPEWEIGHT(examples) === TYPEWEIGHT_STRING) {
|
||||||
|
// a callback function was supplied. this is considered valid
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (TYPEWEIGHT(examples) !== TYPEWEIGHT_LIST) {
|
if (TYPEWEIGHT(examples) !== TYPEWEIGHT_LIST) {
|
||||||
THROW(INTERNAL.errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH, func);
|
THROW(INTERNAL.errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH, func);
|
||||||
|
@ -3793,15 +3798,29 @@ function TRAVERSAL_CHECK_EXAMPLES_TYPEWEIGHTS (examples, func) {
|
||||||
/// @brief traverse a graph
|
/// @brief traverse a graph
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
function TRAVERSAL_FUNC (func, vertexCollection, edgeCollection, startVertex, direction, params) {
|
function TRAVERSAL_FUNC (func,
|
||||||
|
vertexCollection,
|
||||||
|
edgeCollection,
|
||||||
|
startVertex,
|
||||||
|
endVertex,
|
||||||
|
direction,
|
||||||
|
params) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
if (startVertex.indexOf('/') === -1) {
|
if (startVertex.indexOf('/') === -1) {
|
||||||
startVertex = vertexCollection + '/' + startVertex;
|
startVertex = vertexCollection + '/' + startVertex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (endVertex !== undefined && endVertex.indexOf('/') === -1) {
|
||||||
|
endVertex = vertexCollection + '/' + endVertex;
|
||||||
|
}
|
||||||
|
|
||||||
vertexCollection = COLLECTION(vertexCollection);
|
vertexCollection = COLLECTION(vertexCollection);
|
||||||
edgeCollection = COLLECTION(edgeCollection);
|
edgeCollection = COLLECTION(edgeCollection);
|
||||||
|
|
||||||
|
if (params === undefined) {
|
||||||
|
params = { };
|
||||||
|
}
|
||||||
|
|
||||||
// check followEdges property
|
// check followEdges property
|
||||||
if (params.followEdges) {
|
if (params.followEdges) {
|
||||||
|
@ -3822,6 +3841,7 @@ function TRAVERSAL_FUNC (func, vertexCollection, edgeCollection, startVertex, di
|
||||||
}
|
}
|
||||||
|
|
||||||
var config = {
|
var config = {
|
||||||
|
distance: params.distance,
|
||||||
connect: params.connect,
|
connect: params.connect,
|
||||||
datasource: TRAVERSAL.collectionDatasourceFactory(edgeCollection),
|
datasource: TRAVERSAL.collectionDatasourceFactory(edgeCollection),
|
||||||
trackPaths: params.paths || false,
|
trackPaths: params.paths || false,
|
||||||
|
@ -3830,39 +3850,104 @@ function TRAVERSAL_FUNC (func, vertexCollection, edgeCollection, startVertex, di
|
||||||
minDepth: params.minDepth,
|
minDepth: params.minDepth,
|
||||||
maxIterations: params.maxIterations,
|
maxIterations: params.maxIterations,
|
||||||
uniqueness: params.uniqueness,
|
uniqueness: params.uniqueness,
|
||||||
expander: direction
|
expander: direction,
|
||||||
|
strategy: params.strategy
|
||||||
};
|
};
|
||||||
|
|
||||||
if (params.followEdges) {
|
if (params.followEdges) {
|
||||||
config.expandFilter = TRAVERSAL_FILTER;
|
if (typeof params.followEdges === 'string') {
|
||||||
config.expandEdgeExamples = params.followEdges;
|
var f1 = params.followEdges.toUpperCase();
|
||||||
|
config.expandFilter = function (config, vertex, edge, path) {
|
||||||
|
return FCALL_USER(f1, [ config, vertex, edge, path ]);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
config.expandFilter = TRAVERSAL_EDGE_EXAMPLE_FILTER;
|
||||||
|
config.expandEdgeExamples = params.followEdges;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.filterVertices) {
|
if (params.filterVertices) {
|
||||||
config.filter = TRAVERSAL_VERTEX_FILTER;
|
if (typeof params.filterVertices === 'string') {
|
||||||
config.filterVertexExamples = params.filterVertices;
|
var f2 = params.filterVertices.toUpperCase();
|
||||||
config.vertexFilterMethod = params.vertexFilterMethod || ["prune","exclude"];
|
config.filter = function (config, vertex, edge, path) {
|
||||||
|
return FCALL_USER(f2, [ config, vertex, path ]);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
config.filter = TRAVERSAL_VERTEX_FILTER;
|
||||||
|
config.filterVertexExamples = params.filterVertices;
|
||||||
|
config.vertexFilterMethod = params.vertexFilterMethod || ["prune", "exclude"];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params._sort) {
|
if (params._sort) {
|
||||||
config.sort = function (l, r) { return l._key < r._key ? -1 : 1; };
|
config.sort = function (l, r) { return l._key < r._key ? -1 : 1; };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// start vertex
|
||||||
var v = null;
|
var v = null;
|
||||||
var result = [ ];
|
|
||||||
try {
|
try {
|
||||||
v = INTERNAL.db._document(startVertex);
|
v = INTERNAL.db._document(startVertex);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err1) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// end vertex
|
||||||
|
var e;
|
||||||
|
if (endVertex !== undefined) {
|
||||||
|
try {
|
||||||
|
e = INTERNAL.db._document(endVertex);
|
||||||
|
}
|
||||||
|
catch (err2) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = [ ];
|
||||||
if (v !== null) {
|
if (v !== null) {
|
||||||
var traverser = new TRAVERSAL.Traverser(config);
|
var traverser = new TRAVERSAL.Traverser(config);
|
||||||
traverser.traverse(result, v);
|
traverser.traverse(result, v, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief shortest path algorithm
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function GRAPH_SHORTEST_PATH (vertexCollection,
|
||||||
|
edgeCollection,
|
||||||
|
startVertex,
|
||||||
|
endVertex,
|
||||||
|
direction,
|
||||||
|
params) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
params.strategy = "dijkstra";
|
||||||
|
params.itemorder = "forward";
|
||||||
|
params.order = "forward";
|
||||||
|
params.visitor = TRAVERSAL_VISITOR;
|
||||||
|
|
||||||
|
if (typeof params.distance === "string") {
|
||||||
|
var name = params.distance.toUpperCase();
|
||||||
|
|
||||||
|
params.distance = function (config, vertex1, vertex2, edge) {
|
||||||
|
return FCALL_USER(name, [ config, vertex1, vertex2, edge ]);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
params.distance = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRAVERSAL_FUNC("SHORTEST_PATH",
|
||||||
|
vertexCollection,
|
||||||
|
edgeCollection,
|
||||||
|
startVertex,
|
||||||
|
endVertex,
|
||||||
|
direction,
|
||||||
|
params);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief traverse a graph
|
/// @brief traverse a graph
|
||||||
|
@ -3880,7 +3965,8 @@ function GRAPH_TRAVERSAL (vertexCollection,
|
||||||
return TRAVERSAL_FUNC("TRAVERSAL",
|
return TRAVERSAL_FUNC("TRAVERSAL",
|
||||||
vertexCollection,
|
vertexCollection,
|
||||||
edgeCollection,
|
edgeCollection,
|
||||||
startVertex,
|
startVertex,
|
||||||
|
undefined,
|
||||||
direction,
|
direction,
|
||||||
params);
|
params);
|
||||||
}
|
}
|
||||||
|
@ -3903,8 +3989,10 @@ function GRAPH_TRAVERSAL_TREE (vertexCollection,
|
||||||
THROW(INTERNAL.errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH, "TRAVERSAL_TREE");
|
THROW(INTERNAL.errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH, "TRAVERSAL_TREE");
|
||||||
}
|
}
|
||||||
|
|
||||||
params.strategy = "depthfirst";
|
if (params === undefined) {
|
||||||
params.order = "preorder";
|
params = { };
|
||||||
|
}
|
||||||
|
|
||||||
params.visitor = TRAVERSAL_TREE_VISITOR;
|
params.visitor = TRAVERSAL_TREE_VISITOR;
|
||||||
params.connect = connectName;
|
params.connect = connectName;
|
||||||
|
|
||||||
|
@ -3912,6 +4000,7 @@ function GRAPH_TRAVERSAL_TREE (vertexCollection,
|
||||||
vertexCollection,
|
vertexCollection,
|
||||||
edgeCollection,
|
edgeCollection,
|
||||||
startVertex,
|
startVertex,
|
||||||
|
undefined,
|
||||||
direction,
|
direction,
|
||||||
params);
|
params);
|
||||||
|
|
||||||
|
@ -4103,6 +4192,7 @@ exports.GEO_NEAR = GEO_NEAR;
|
||||||
exports.GEO_WITHIN = GEO_WITHIN;
|
exports.GEO_WITHIN = GEO_WITHIN;
|
||||||
exports.FULLTEXT = FULLTEXT;
|
exports.FULLTEXT = FULLTEXT;
|
||||||
exports.GRAPH_PATHS = GRAPH_PATHS;
|
exports.GRAPH_PATHS = GRAPH_PATHS;
|
||||||
|
exports.GRAPH_SHORTEST_PATH = GRAPH_SHORTEST_PATH;
|
||||||
exports.GRAPH_TRAVERSAL = GRAPH_TRAVERSAL;
|
exports.GRAPH_TRAVERSAL = GRAPH_TRAVERSAL;
|
||||||
exports.GRAPH_TRAVERSAL_TREE = GRAPH_TRAVERSAL_TREE;
|
exports.GRAPH_TRAVERSAL_TREE = GRAPH_TRAVERSAL_TREE;
|
||||||
exports.GRAPH_EDGES = GRAPH_EDGES;
|
exports.GRAPH_EDGES = GRAPH_EDGES;
|
||||||
|
|
|
@ -187,7 +187,7 @@ extend(RequestContext.prototype, {
|
||||||
|
|
||||||
constraint[paramName] = this.typeToRegex[attributes.type];
|
constraint[paramName] = this.typeToRegex[attributes.type];
|
||||||
if (!constraint[paramName]) {
|
if (!constraint[paramName]) {
|
||||||
throw new Error("Illegal attribute type: " + attributes.type);
|
throw new Error("Illegal attribute type: " + attributes.type);
|
||||||
}
|
}
|
||||||
this.route.url = internal.constructUrlObject(url.match, constraint, url.methods[0]);
|
this.route.url = internal.constructUrlObject(url.match, constraint, url.methods[0]);
|
||||||
this.docs.addPathParam(paramName, attributes.description, attributes.type);
|
this.docs.addPathParam(paramName, attributes.description, attributes.type);
|
||||||
|
|
|
@ -473,13 +473,239 @@ function ahuacatlQueryPathsTestSuite () {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test suite for SHORTEST_PATH() function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function ahuacatlQueryShortestPathTestSuite () {
|
||||||
|
var vn = "UnitTestsTraversalVertices";
|
||||||
|
var en = "UnitTestsTraversalEdges";
|
||||||
|
var vertexCollection;
|
||||||
|
var edgeCollection;
|
||||||
|
|
||||||
|
var aqlfunctions = require("org/arangodb/aql/functions");
|
||||||
|
|
||||||
|
return {
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief set up
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
setUp : function () {
|
||||||
|
db._drop(vn);
|
||||||
|
db._drop(en);
|
||||||
|
|
||||||
|
vertexCollection = db._create(vn);
|
||||||
|
edgeCollection = db._createEdgeCollection(en);
|
||||||
|
|
||||||
|
[ "A", "B", "C", "D", "E", "F", "G", "H" ].forEach(function (item) {
|
||||||
|
vertexCollection.save({ _key: item, name: item });
|
||||||
|
});
|
||||||
|
|
||||||
|
[ [ "A", "B", 1 ], [ "B", "C", 5 ], [ "C", "D", 1 ], [ "A", "D", 12 ], [ "D", "C", 3 ], [ "C", "B", 2 ], [ "D", "E", 6 ], [ "B", "F", 1 ], [ "E", "G", 5 ], [ "G", "H", 2 ] ].forEach(function (item) {
|
||||||
|
var l = item[0];
|
||||||
|
var r = item[1];
|
||||||
|
var w = item[2];
|
||||||
|
edgeCollection.save(vn + "/" + l, vn + "/" + r, { _key: l + r, what : l + "->" + r, weight: w });
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
aqlfunctions.unregister("UnitTests::distance");
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief tear down
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
tearDown : function () {
|
||||||
|
db._drop(vn);
|
||||||
|
db._drop(en);
|
||||||
|
|
||||||
|
vertexCollection = null;
|
||||||
|
edgeCollection = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
aqlfunctions.unregister("UnitTests::distance");
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief shortest path using dijkstra
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testShortestPathDijkstraOutbound : function () {
|
||||||
|
var config = {
|
||||||
|
paths: true,
|
||||||
|
_sort: true
|
||||||
|
};
|
||||||
|
|
||||||
|
var actual = getQueryResults("FOR p IN SHORTEST_PATH(@@v, @@e, '" + vn + "/A', '" + vn + "/H', 'outbound', " + JSON.stringify(config) + ") RETURN [ p.vertex._key, p.path.vertices[*]._key, p.path.edges[*]._key ]", { "@v" : vn, "@e" : en });
|
||||||
|
|
||||||
|
assertEqual([
|
||||||
|
[ "A", [ "A" ], [ ] ],
|
||||||
|
[ "D", [ "A", "D" ], [ "AD" ] ],
|
||||||
|
[ "E", [ "A", "D", "E" ], [ "AD", "DE" ] ],
|
||||||
|
[ "G", [ "A", "D", "E", "G" ], [ "AD", "DE", "EG" ] ],
|
||||||
|
[ "H", [ "A", "D", "E", "G", "H" ], [ "AD", "DE", "EG", "GH" ] ]
|
||||||
|
], actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief shortest path using dijkstra
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testShortestPathDijkstraInbound : function () {
|
||||||
|
var config = {
|
||||||
|
_sort: true
|
||||||
|
};
|
||||||
|
|
||||||
|
var actual = getQueryResults("FOR p IN SHORTEST_PATH(@@v, @@e, '" + vn + "/H', '" + vn + "/A', 'inbound', " + JSON.stringify(config) + ") RETURN p.vertex._key", { "@v" : vn, "@e" : en });
|
||||||
|
|
||||||
|
assertEqual([ "H", "G", "E", "D", "A" ], actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief shortest path with custom distance function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testShortestPathDijkstraDistance : function () {
|
||||||
|
aqlfunctions.register("UnitTests::distance", function (config, vertex1, vertex2, edge) {
|
||||||
|
return edge.weight;
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
distance: "UnitTests::distance",
|
||||||
|
_sort: true
|
||||||
|
};
|
||||||
|
|
||||||
|
var actual = getQueryResults("FOR p IN SHORTEST_PATH(@@v, @@e, '" + vn + "/A', '" + vn + "/H', 'outbound', " + JSON.stringify(config) + ") RETURN p.vertex._key", { "@v" : vn, "@e" : en });
|
||||||
|
|
||||||
|
assertEqual([ "A", "B", "C", "D", "E", "G", "H" ], actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief shortest path with vertex filter function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testShortestPathDijkstraVertexFilter1 : function () {
|
||||||
|
aqlfunctions.register("UnitTests::distance", function (config, vertex, path) {
|
||||||
|
if (vertex._key === 'B' || vertex._key === 'C') {
|
||||||
|
return [ 'exclude', 'prune' ];
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
filterVertices: "UnitTests::distance",
|
||||||
|
_sort: true
|
||||||
|
};
|
||||||
|
|
||||||
|
var actual = getQueryResults("FOR p IN SHORTEST_PATH(@@v, @@e, '" + vn + "/A', '" + vn + "/H', 'outbound', " + JSON.stringify(config) + ") RETURN p.vertex._key", { "@v" : vn, "@e" : en });
|
||||||
|
|
||||||
|
assertEqual([ "A", "D", "E", "G", "H" ], actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief shortest path with vertex filter function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testShortestPathDijkstraVertexFilter2 : function () {
|
||||||
|
aqlfunctions.register("UnitTests::distance", function (config, vertex, path) {
|
||||||
|
return [ 'exclude', 'prune' ];
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
filterVertices: "UnitTests::distance",
|
||||||
|
_sort: true
|
||||||
|
};
|
||||||
|
|
||||||
|
var actual = getQueryResults("FOR p IN SHORTEST_PATH(@@v, @@e, '" + vn + "/A', '" + vn + "/H', 'outbound', " + JSON.stringify(config) + ") RETURN p.vertex._key", { "@v" : vn, "@e" : en });
|
||||||
|
|
||||||
|
assertEqual([ ], actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief shortest path with edge filter function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testShortestPathDijkstraEdgeFilter : function () {
|
||||||
|
aqlfunctions.register("UnitTests::distance", function (config, vertex, edge, path) {
|
||||||
|
return edge.weight <= 6;
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
followEdges: "UnitTests::distance",
|
||||||
|
_sort: true
|
||||||
|
};
|
||||||
|
|
||||||
|
var actual = getQueryResults("FOR p IN SHORTEST_PATH(@@v, @@e, '" + vn + "/A', '" + vn + "/H', 'outbound', " + JSON.stringify(config) + ") RETURN p.vertex._key", { "@v" : vn, "@e" : en });
|
||||||
|
|
||||||
|
assertEqual([ "A", "B", "C", "D", "E", "G", "H" ], actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief shortest path, with cycles
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testShortestPathDijkstraCycles : function () {
|
||||||
|
[ [ "B", "A" ], [ "C", "A" ], [ "D", "B" ] ].forEach(function (item) {
|
||||||
|
var l = item[0];
|
||||||
|
var r = item[1];
|
||||||
|
edgeCollection.save(vn + "/" + l, vn + "/" + r, { _key: l + r, what : l + "->" + r });
|
||||||
|
});
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
paths: true,
|
||||||
|
_sort: true
|
||||||
|
};
|
||||||
|
|
||||||
|
var actual = getQueryResults("FOR p IN SHORTEST_PATH(@@v, @@e, '" + vn + "/A', '" + vn + "/H', 'outbound', " + JSON.stringify(config) + ") RETURN [ p.vertex._key, p.path.vertices[*]._key, p.path.edges[*]._key ]", { "@v" : vn, "@e" : en });
|
||||||
|
|
||||||
|
assertEqual([
|
||||||
|
[ "A", [ "A" ], [ ] ],
|
||||||
|
[ "D", [ "A", "D" ], [ "AD" ] ],
|
||||||
|
[ "E", [ "A", "D", "E" ], [ "AD", "DE" ] ],
|
||||||
|
[ "G", [ "A", "D", "E", "G" ], [ "AD", "DE", "EG" ] ],
|
||||||
|
[ "H", [ "A", "D", "E", "G", "H" ], [ "AD", "DE", "EG", "GH" ] ]
|
||||||
|
], actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief shortest path, non-connected vertices
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testShortestPathDijkstraNotConnected : function () {
|
||||||
|
// this item is not connected to any other
|
||||||
|
vertexCollection.save({ _key: "J", name: "J" });
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
paths: true,
|
||||||
|
_sort: true
|
||||||
|
};
|
||||||
|
|
||||||
|
var actual = getQueryResults("FOR p IN SHORTEST_PATH(@@v, @@e, '" + vn + "/A', '" + vn + "/J', 'outbound', " + JSON.stringify(config) + ") RETURN p.vertex._key", { "@v" : vn, "@e" : en });
|
||||||
|
|
||||||
|
assertEqual([ ], actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief test suite for TRAVERSAL() filter function
|
/// @brief test suite for TRAVERSAL() filter function
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
function ahuacatlQueryTraversalFilterTestSuite () {
|
function ahuacatlQueryTraversalFilterTestSuite () {
|
||||||
var vn = "UnitTestsTraverseVertices";
|
var vn = "UnitTestsTraversalVertices";
|
||||||
var en = "UnitTestsTraverseEdges";
|
var en = "UnitTestsTraversalEdges";
|
||||||
|
var vertexCollection;
|
||||||
|
var edgeCollection;
|
||||||
|
|
||||||
|
var aqlfunctions = require("org/arangodb/aql/functions");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
||||||
|
@ -514,6 +740,12 @@ function ahuacatlQueryTraversalFilterTestSuite () {
|
||||||
var r = item[1];
|
var r = item[1];
|
||||||
edgeCollection.save(vn + "/" + l, vn + "/" + r, { _key: l + r, what : l + "->" + r });
|
edgeCollection.save(vn + "/" + l, vn + "/" + r, { _key: l + r, what : l + "->" + r });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
aqlfunctions.unregister("UnitTests::filter");
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -523,6 +755,41 @@ function ahuacatlQueryTraversalFilterTestSuite () {
|
||||||
tearDown : function () {
|
tearDown : function () {
|
||||||
db._drop(vn);
|
db._drop(vn);
|
||||||
db._drop(en);
|
db._drop(en);
|
||||||
|
|
||||||
|
try {
|
||||||
|
aqlfunctions.unregister("UnitTests::filter");
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test vertex filter with custom function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testTraversalVertexFilterCallback : function () {
|
||||||
|
aqlfunctions.register("UnitTests::filter", function (config, vertex, path) {
|
||||||
|
if (vertex._key.substr(0, 3) === '1-1') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return 'exclude';
|
||||||
|
});
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
strategy: "depthfirst",
|
||||||
|
order: "preorder",
|
||||||
|
itemOrder: "forward",
|
||||||
|
uniqueness: {
|
||||||
|
vertices: "global",
|
||||||
|
edges: "none"
|
||||||
|
},
|
||||||
|
_sort: true,
|
||||||
|
filterVertices: "UnitTests::filter"
|
||||||
|
};
|
||||||
|
|
||||||
|
var actual = getQueryResults("FOR p IN TRAVERSAL(@@v, @@e, '" + vn + "/1', 'outbound', " + JSON.stringify(config) + ") RETURN p.vertex._key", { "@v" : vn, "@e" : en });
|
||||||
|
|
||||||
|
assertEqual([ "1-1", "1-1-1", "1-1-2" ], actual);
|
||||||
},
|
},
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -644,6 +911,32 @@ function ahuacatlQueryTraversalFilterTestSuite () {
|
||||||
|
|
||||||
var actual = getQueryResults("FOR p IN TRAVERSAL(@@v, @@e, '" + vn + "/1', 'outbound', " + JSON.stringify(config) + ") RETURN p.vertex._key", { "@v" : vn, "@e" : en });
|
var actual = getQueryResults("FOR p IN TRAVERSAL(@@v, @@e, '" + vn + "/1', 'outbound', " + JSON.stringify(config) + ") RETURN p.vertex._key", { "@v" : vn, "@e" : en });
|
||||||
assertEqual([ "1", "1-1", "1-1-2", "1-2-1", "1-3", "1-3-2" ], actual);
|
assertEqual([ "1", "1-1", "1-1-2", "1-2-1", "1-3", "1-3-2" ], actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test edge filter with custom function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testTraversalEdgeFilterCallback : function () {
|
||||||
|
aqlfunctions.register("UnitTests::filter", function (config, vertex, edge, path) {
|
||||||
|
return ! vertex.isDeleted;
|
||||||
|
});
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
strategy: "depthfirst",
|
||||||
|
order: "preorder",
|
||||||
|
itemOrder: "forward",
|
||||||
|
uniqueness: {
|
||||||
|
vertices: "global",
|
||||||
|
edges: "none"
|
||||||
|
},
|
||||||
|
_sort: true,
|
||||||
|
followEdges: "UnitTests::filter"
|
||||||
|
};
|
||||||
|
|
||||||
|
var actual = getQueryResults("FOR p IN TRAVERSAL(@@v, @@e, '" + vn + "/1', 'outbound', " + JSON.stringify(config) + ") RETURN p.vertex._key", { "@v" : vn, "@e" : en });
|
||||||
|
|
||||||
|
assertEqual([ "1", "1-1", "1-1-2", "1-3", "1-3-2" ], actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -654,11 +947,10 @@ function ahuacatlQueryTraversalFilterTestSuite () {
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
function ahuacatlQueryTraversalTestSuite () {
|
function ahuacatlQueryTraversalTestSuite () {
|
||||||
var vn = "UnitTestsTraverseVertices";
|
var vn = "UnitTestsTraversalVertices";
|
||||||
var en = "UnitTestsTraverseEdges";
|
var en = "UnitTestsTraversalEdges";
|
||||||
|
var vertexCollection;
|
||||||
var vertices;
|
var edgeCollection;
|
||||||
var edges;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
||||||
|
@ -693,6 +985,51 @@ function ahuacatlQueryTraversalTestSuite () {
|
||||||
db._drop(en);
|
db._drop(en);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test min-depth filtering
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testTraversalBreadthFirst : function () {
|
||||||
|
var config = {
|
||||||
|
strategy: "breadthfirst",
|
||||||
|
order: "preorder",
|
||||||
|
itemOrder: "forward",
|
||||||
|
uniqueness: {
|
||||||
|
vertices: "none",
|
||||||
|
edges: "path"
|
||||||
|
},
|
||||||
|
_sort: true
|
||||||
|
};
|
||||||
|
|
||||||
|
var actual = getQueryResults("FOR p IN TRAVERSAL(@@v, @@e, '" + vn + "/A', 'outbound', " + JSON.stringify(config) + ") RETURN p.vertex._key", { "@v" : vn, "@e" : en });
|
||||||
|
|
||||||
|
assertEqual([ "A", "B", "D", "C", "C", "A", "A", "D", "B", "C", "C" ], actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test min-depth filtering, track paths
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testTraversalTrackPaths : function () {
|
||||||
|
var config = {
|
||||||
|
paths: true,
|
||||||
|
minDepth: 1,
|
||||||
|
uniqueness: {
|
||||||
|
vertices: "global",
|
||||||
|
edges: "none"
|
||||||
|
},
|
||||||
|
_sort: true
|
||||||
|
};
|
||||||
|
|
||||||
|
var actual = getQueryResults("FOR p IN TRAVERSAL(@@v, @@e, '" + vn + "/A', 'outbound', " + JSON.stringify(config) + ") RETURN [ p.vertex._key, p.path.vertices[*]._key, p.path.edges[*]._key ]", { "@v" : vn, "@e" : en });
|
||||||
|
|
||||||
|
assertEqual([
|
||||||
|
[ "B", [ "A", "B" ], [ "AB" ] ],
|
||||||
|
[ "C", [ "A", "B", "C" ], [ "AB", "BC" ] ],
|
||||||
|
[ "D", [ "A", "D" ], [ "AD" ] ]
|
||||||
|
], actual);
|
||||||
|
},
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief test min-depth filtering
|
/// @brief test min-depth filtering
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -910,11 +1247,10 @@ function ahuacatlQueryTraversalTestSuite () {
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
function ahuacatlQueryTraversalTreeTestSuite () {
|
function ahuacatlQueryTraversalTreeTestSuite () {
|
||||||
var vn = "UnitTestsTraverseVertices";
|
var vn = "UnitTestsTraversalVertices";
|
||||||
var en = "UnitTestsTraverseEdges";
|
var en = "UnitTestsTraversalEdges";
|
||||||
|
var vertexCollection;
|
||||||
var vertices;
|
var edgeCollection;
|
||||||
var edges;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
||||||
|
@ -994,6 +1330,7 @@ function ahuacatlQueryTraversalTreeTestSuite () {
|
||||||
|
|
||||||
jsunity.run(ahuacatlQueryEdgesTestSuite);
|
jsunity.run(ahuacatlQueryEdgesTestSuite);
|
||||||
jsunity.run(ahuacatlQueryPathsTestSuite);
|
jsunity.run(ahuacatlQueryPathsTestSuite);
|
||||||
|
jsunity.run(ahuacatlQueryShortestPathTestSuite);
|
||||||
jsunity.run(ahuacatlQueryTraversalFilterTestSuite);
|
jsunity.run(ahuacatlQueryTraversalFilterTestSuite);
|
||||||
jsunity.run(ahuacatlQueryTraversalTestSuite);
|
jsunity.run(ahuacatlQueryTraversalTestSuite);
|
||||||
jsunity.run(ahuacatlQueryTraversalTreeTestSuite);
|
jsunity.run(ahuacatlQueryTraversalTreeTestSuite);
|
||||||
|
|
Loading…
Reference in New Issue