1
0
Fork 0

allow specifying minLength and maxLengths for PATHS AQL function

This commit is contained in:
Jan Steemann 2015-01-16 11:53:59 +01:00
parent 9049753b62
commit ba783a4584
4 changed files with 221 additions and 12 deletions

View File

@ -5,7 +5,7 @@ AQL has the following functions to traverse graphs:
If you have created a graph in the general-graph module you may want to use
[Graph operations](../Aql/GraphOperations.md) instead.
- *PATHS(vertexcollection, edgecollection, direction, followcycles)*:
- *PATHS(vertexcollection, edgecollection, direction, options)*:
returns an array of paths through the graph defined by the nodes in the collection
*vertexcollection* and edges in the collection *edgecollection*. For each vertex
in *vertexcollection*, it will determine the paths through the graph depending on the
@ -14,8 +14,11 @@ If you have created a graph in the general-graph module you may want to use
- *"inbound"*: Follow all paths that lead from another vertex to the current vertex
- *"any"*: Combination of *"outbound"* and *"inbound"*
The default value for *direction* is *"outbound"*.
If *followcycles* is true, cyclic paths will be followed as well. This is turned off by
default.
If specified, *options* must be a JavaScript object with the following optional attributes:
- *minLength*: specifies the minimum length of the paths to be returned. The default is 0.
- *maxLength*: specifies the maximum length of the paths to be returned. The default is 10.
- *followcycles*: if true, cyclic paths will be followed as well. This is turned off by
default.
The result of the function is an array of paths. Paths of length 0 will also be returned. Each
path is a document consisting of the following attributes:
@ -26,10 +29,10 @@ If you have created a graph in the general-graph module you may want to use
*Examples*
PATHS(friends, friendrelations, "outbound", false)
PATHS(friends, friendrelations, "outbound")
FOR p IN PATHS(friends, friendrelations, "outbound")
FILTER p.source._id == "123456/123456" && LENGTH(p.edges) == 2
FOR p IN PATHS(friends, friendrelations, "outbound", { minLength: 2, maxLength: 2 })
FILTER p.source._id == "users/123456"
RETURN p.vertices[*].name
If you have created a graph in the general-graph module you may want to use

View File

@ -195,7 +195,7 @@ std::unordered_map<std::string, Function const> const Executor::FunctionNames{
{ "FULLTEXT", Function("FULLTEXT", "AQL_FULLTEXT", "h,s,s", false, true, false) },
// graph functions
{ "PATHS", Function("PATHS", "AQL_PATHS", "c,h|s,b", false, true, false) },
{ "PATHS", Function("PATHS", "AQL_PATHS", "c,h|s,ba", false, true, false) },
{ "GRAPH_PATHS", Function("GRAPH_PATHS", "AQL_GRAPH_PATHS", "s|a", false, true, false) },
{ "SHORTEST_PATH", Function("SHORTEST_PATH", "AQL_SHORTEST_PATH", "h,h,s,s,s|a", false, true, false) },
{ "GRAPH_SHORTEST_PATH", Function("GRAPH_SHORTEST_PATH", "AQL_GRAPH_SHORTEST_PATH", "s,als,als|a", false, true, false) },

View File

@ -4523,15 +4523,22 @@ function SUBNODES (searchAttributes, vertexId, visited, edges, vertices, level)
/// @brief find all paths through a graph
////////////////////////////////////////////////////////////////////////////////
function AQL_PATHS (vertices, edgeCollection, direction, followCycles, minLength, maxLength) {
function AQL_PATHS (vertices, edgeCollection, direction, options) {
"use strict";
var searchDirection;
direction = direction || "outbound";
followCycles = followCycles || false;
minLength = minLength || 0;
maxLength = maxLength !== undefined ? maxLength : 10;
if (typeof options === "boolean") {
options = { followCycles : options };
}
else if (typeof options !== "object" || Array.isArray(options)) {
options = { };
}
var followCycles = options.followCycles || false;
var minLength = options.minLength || 0;
var maxLength = options.maxLength || 10;
if (TYPEWEIGHT(vertices) !== TYPEWEIGHT_ARRAY) {
WARN("PATHS", INTERNAL.errors.ERROR_QUERY_ARRAY_EXPECTED);
@ -4539,6 +4546,7 @@ function AQL_PATHS (vertices, edgeCollection, direction, followCycles, minLength
}
// validate arguments
var searchDirection;
if (direction === "outbound") {
searchDirection = 1;
}

View File

@ -314,6 +314,22 @@ function ahuacatlQueryPathsTestSuite () {
assertEqual(docs["Fred"]._id, actual[0].edges[0]._to);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks an AQL outbound query using _from, path length 1
////////////////////////////////////////////////////////////////////////////////
testFromQueryOutboundMaxLength1 : function () {
var actual = getQueryResults("FOR p IN PATHS(UnitTestsAhuacatlUsers, UnitTestsAhuacatlUserRelations, \"outbound\", { maxLength: 1 }) FILTER p.edges[0]._from == \"" + docs["John"]._id +"\" RETURN p");
assertEqual(1, actual.length);
assertEqual(1, actual[0].edges.length);
assertEqual(docs["John"]._id, actual[0].source._id);
assertEqual(docs["Fred"]._id, actual[0].destination._id);
assertEqual(docs["John"]._id, actual[0].edges[0]._from);
assertEqual(docs["Fred"]._id, actual[0].edges[0]._to);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks an AQL outbound query using _from, path length 1
////////////////////////////////////////////////////////////////////////////////
@ -330,6 +346,22 @@ function ahuacatlQueryPathsTestSuite () {
assertEqual(docs["Jacob"]._id, actual[0].edges[0]._to);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks an AQL outbound query using _from, path length 1
////////////////////////////////////////////////////////////////////////////////
testFromQueryOutboundMaxLength2 : function () {
var actual = getQueryResults("FOR p IN PATHS(UnitTestsAhuacatlUsers, UnitTestsAhuacatlUserRelations, \"outbound\", { maxLength: 1 }) FILTER p.edges[0]._from == \"" + docs["Fred"]._id +"\" RETURN p");
assertEqual(1, actual.length);
assertEqual(1, actual[0].edges.length);
assertEqual(docs["Fred"]._id, actual[0].source._id);
assertEqual(docs["Jacob"]._id, actual[0].destination._id);
assertEqual(docs["Fred"]._id, actual[0].edges[0]._from);
assertEqual(docs["Jacob"]._id, actual[0].edges[0]._to);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks an AQL outbound query using _to, path length 1
////////////////////////////////////////////////////////////////////////////////
@ -346,6 +378,22 @@ function ahuacatlQueryPathsTestSuite () {
assertEqual(docs["Fred"]._id, actual[0].edges[0]._to);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks an AQL outbound query using _to, path length 1
////////////////////////////////////////////////////////////////////////////////
testFromQueryOutboundMaxLength3 : function () {
var actual = getQueryResults("FOR p IN PATHS(UnitTestsAhuacatlUsers, UnitTestsAhuacatlUserRelations, \"outbound\", { maxLength: 1 }) FILTER p.edges[0]._to == \"" + docs["Fred"]._id +"\" RETURN p");
assertEqual(1, actual.length);
assertEqual(1, actual[0].edges.length);
assertEqual(docs["John"]._id, actual[0].source._id);
assertEqual(docs["Fred"]._id, actual[0].destination._id);
assertEqual(docs["John"]._id, actual[0].edges[0]._from);
assertEqual(docs["Fred"]._id, actual[0].edges[0]._to);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks an AQL outbound query using _from, path length 2
////////////////////////////////////////////////////////////////////////////////
@ -364,6 +412,24 @@ function ahuacatlQueryPathsTestSuite () {
assertEqual(docs["Jacob"]._id, actual[0].edges[1]._to);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks an AQL outbound query using _from, path length 2
////////////////////////////////////////////////////////////////////////////////
testFromQueryOutboundMaxLength4 : function () {
var actual = getQueryResults("FOR p IN PATHS(UnitTestsAhuacatlUsers, UnitTestsAhuacatlUserRelations, \"outbound\", { minLength: 2, maxLength: 2 }) FILTER p.edges[0]._from == \"" + docs["John"]._id +"\" RETURN p");
assertEqual(1, actual.length);
assertEqual(2, actual[0].edges.length);
assertEqual(docs["John"]._id, actual[0].source._id);
assertEqual(docs["Jacob"]._id, actual[0].destination._id);
assertEqual(docs["John"]._id, actual[0].edges[0]._from);
assertEqual(docs["Fred"]._id, actual[0].edges[0]._to);
assertEqual(docs["Fred"]._id, actual[0].edges[1]._from);
assertEqual(docs["Jacob"]._id, actual[0].edges[1]._to);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks an AQL outbound query using _from
////////////////////////////////////////////////////////////////////////////////
@ -373,6 +439,15 @@ function ahuacatlQueryPathsTestSuite () {
assertEqual(0, actual.length);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks an AQL outbound query using _from
////////////////////////////////////////////////////////////////////////////////
testFromQueryOutboundMaxLength5 : function () {
var actual = getQueryResults("FOR p IN PATHS(UnitTestsAhuacatlUsers, UnitTestsAhuacatlUserRelations, \"outbound\", { maxLength: 2 }) FILTER p.edges[0]._from == \"" + docs["Jacob"]._id +"\" RETURN p");
assertEqual(0, actual.length);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks an AQL outbound query using _to
////////////////////////////////////////////////////////////////////////////////
@ -382,6 +457,15 @@ function ahuacatlQueryPathsTestSuite () {
assertEqual(0, actual.length);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks an AQL outbound query using _to
////////////////////////////////////////////////////////////////////////////////
testFromQueryOutboundMaxLength6 : function () {
var actual = getQueryResults("FOR p IN PATHS(UnitTestsAhuacatlUsers, UnitTestsAhuacatlUserRelations, \"outbound\", { maxLength: 2 }) FILTER p.edges[0]._to == \"" + docs["John"]._id +"\" RETURN p");
assertEqual(0, actual.length);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks an AQL inbound query using _from, path length 1
////////////////////////////////////////////////////////////////////////////////
@ -398,6 +482,22 @@ function ahuacatlQueryPathsTestSuite () {
assertEqual(docs["Fred"]._id, actual[0].edges[0]._to);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks an AQL inbound query using _from, path length 1
////////////////////////////////////////////////////////////////////////////////
testFromQueryInboundMaxLength1 : function () {
var actual = getQueryResults("FOR p IN PATHS(UnitTestsAhuacatlUsers, UnitTestsAhuacatlUserRelations, \"inbound\", { maxLength: 1 }) FILTER p.edges[0]._from == \"" + docs["John"]._id +"\" RETURN p");
assertEqual(1, actual.length);
assertEqual(1, actual[0].edges.length);
assertEqual(docs["Fred"]._id, actual[0].source._id);
assertEqual(docs["John"]._id, actual[0].destination._id);
assertEqual(docs["John"]._id, actual[0].edges[0]._from);
assertEqual(docs["Fred"]._id, actual[0].edges[0]._to);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks an AQL inbound query using _from, path length 1
////////////////////////////////////////////////////////////////////////////////
@ -414,6 +514,22 @@ function ahuacatlQueryPathsTestSuite () {
assertEqual(docs["Jacob"]._id, actual[0].edges[0]._to);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks an AQL inbound query using _from, path length 1
////////////////////////////////////////////////////////////////////////////////
testFromQueryInboundMaxLength2 : function () {
var actual = getQueryResults("FOR p IN PATHS(UnitTestsAhuacatlUsers, UnitTestsAhuacatlUserRelations, \"inbound\", { maxLength: 1 }) FILTER p.edges[0]._from == \"" + docs["Fred"]._id +"\" RETURN p");
assertEqual(1, actual.length);
assertEqual(1, actual[0].edges.length);
assertEqual(docs["Jacob"]._id, actual[0].source._id);
assertEqual(docs["Fred"]._id, actual[0].destination._id);
assertEqual(docs["Fred"]._id, actual[0].edges[0]._from);
assertEqual(docs["Jacob"]._id, actual[0].edges[0]._to);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks an AQL inbound query using _to, path length 1
////////////////////////////////////////////////////////////////////////////////
@ -430,6 +546,22 @@ function ahuacatlQueryPathsTestSuite () {
assertEqual(docs["Fred"]._id, actual[0].edges[0]._to);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks an AQL inbound query using _to, path length 1
////////////////////////////////////////////////////////////////////////////////
testFromQueryInboundMaxLength3 : function () {
var actual = getQueryResults("FOR p IN PATHS(UnitTestsAhuacatlUsers, UnitTestsAhuacatlUserRelations, \"inbound\", { maxLength: 1 }) FILTER p.edges[0]._to == \"" + docs["Fred"]._id +"\" RETURN p");
assertEqual(1, actual.length);
assertEqual(1, actual[0].edges.length);
assertEqual(docs["Fred"]._id, actual[0].source._id);
assertEqual(docs["John"]._id, actual[0].destination._id);
assertEqual(docs["John"]._id, actual[0].edges[0]._from);
assertEqual(docs["Fred"]._id, actual[0].edges[0]._to);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks an AQL inbound query using _from, path length 1
////////////////////////////////////////////////////////////////////////////////
@ -449,6 +581,72 @@ function ahuacatlQueryPathsTestSuite () {
assertEqual(docs["Fred"]._id, actual[0].edges[1]._to);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks an AQL inbound query using _from, path length 1
////////////////////////////////////////////////////////////////////////////////
testFromQueryInboundMaxLength4 : function () {
var actual = getQueryResults("FOR p IN PATHS(UnitTestsAhuacatlUsers, UnitTestsAhuacatlUserRelations, \"inbound\", { minLength: 2, maxLength: 2 }) FILTER p.edges[LENGTH(p.edges) - 1]._from == \"" + docs["John"]._id +"\" RETURN p");
assertEqual(1, actual.length);
assertEqual(2, actual[0].edges.length);
assertEqual(docs["Jacob"]._id, actual[0].source._id);
assertEqual(docs["John"]._id, actual[0].destination._id);
assertEqual(docs["Fred"]._id, actual[0].edges[0]._from);
assertEqual(docs["Jacob"]._id, actual[0].edges[0]._to);
assertEqual(docs["John"]._id, actual[0].edges[1]._from);
assertEqual(docs["Fred"]._id, actual[0].edges[1]._to);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks an AQL inbound query using _from, path length 2
////////////////////////////////////////////////////////////////////////////////
testFromQueryInboundMinLength1 : function () {
var actual = getQueryResults("FOR p IN PATHS(UnitTestsAhuacatlUsers, UnitTestsAhuacatlUserRelations, \"inbound\", { minLength: 2, maxLength: 2 }) FILTER p.edges[LENGTH(p.edges) - 1]._from == \"" + docs["John"]._id +"\" RETURN p");
assertEqual(1, actual.length);
assertEqual(2, actual[0].edges.length);
assertEqual(docs["Jacob"]._id, actual[0].source._id);
assertEqual(docs["John"]._id, actual[0].destination._id);
assertEqual(docs["Fred"]._id, actual[0].edges[0]._from);
assertEqual(docs["Jacob"]._id, actual[0].edges[0]._to);
assertEqual(docs["John"]._id, actual[0].edges[1]._from);
assertEqual(docs["Fred"]._id, actual[0].edges[1]._to);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks an AQL inbound query using _from, path length 2
////////////////////////////////////////////////////////////////////////////////
testFromQueryInboundMinLength2 : function () {
var actual = getQueryResults("FOR p IN PATHS(UnitTestsAhuacatlUsers, UnitTestsAhuacatlUserRelations, \"inbound\", { minLength: 2 }) FILTER p.edges[LENGTH(p.edges) - 1]._from == \"" + docs["John"]._id +"\" RETURN p");
assertEqual(1, actual.length);
assertEqual(2, actual[0].edges.length);
assertEqual(docs["Jacob"]._id, actual[0].source._id);
assertEqual(docs["John"]._id, actual[0].destination._id);
assertEqual(docs["Fred"]._id, actual[0].edges[0]._from);
assertEqual(docs["Jacob"]._id, actual[0].edges[0]._to);
assertEqual(docs["John"]._id, actual[0].edges[1]._from);
assertEqual(docs["Fred"]._id, actual[0].edges[1]._to);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks an AQL inbound query using _from, path length 3
////////////////////////////////////////////////////////////////////////////////
testFromQueryInboundMinLength3 : function () {
var actual = getQueryResults("FOR p IN PATHS(UnitTestsAhuacatlUsers, UnitTestsAhuacatlUserRelations, \"inbound\", { minLength: 3 }) FILTER p.edges[LENGTH(p.edges) - 1]._from == \"" + docs["John"]._id +"\" RETURN p");
assertEqual(0, actual.length);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks AQL inbound and outbound queries
////////////////////////////////////////////////////////////////////////////////