1
0
Fork 0

added AQL function NEIGHBORS

This commit is contained in:
Jan Steemann 2013-04-09 18:14:46 +02:00
parent 1d98c4b0f0
commit badf20e47e
5 changed files with 212 additions and 10 deletions

View File

@ -103,6 +103,12 @@ v1.3.alpha1 (2013-04-05)
v1.2.3 (XXXX-XX-XX)
-------------------
* added optional parameter `edgexamples` for AQL function EDGES() and NEIGHBORS()
* added AQL function NEIGHBORS()
* added freebsd support
* fixed firstExample() query with `_id` and `_key` attributes
* issue triAGENS/ArangoDB-PHP#55: AQL optimiser may have mis-optimised duplicate

View File

@ -1007,7 +1007,8 @@ AQL supports the following functions to operate on document values:
continue the comparison with the next example until there are no more examples left.
The @FA{examples} must be a list of 1..n example documents, with any number of attributes
each.
each. Note: specifying an empty list of examples is not allowed.
Example usage:
RETURN MATCHES({ "test" : 1 }, [
@ -1287,16 +1288,39 @@ 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
time and memory for the result set.
- @FN{EDGES(@FA{edgecollection}\, @FA{startvertex}\, @FA{direction})}:
- @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
direction are:
@FA{direction} are:
- `outbound`: return all outbound edges
- `inbound`: return all inbound edges
- `any`: return outbound and inbound edges
The @FA{edgeexamples} parameter can optionally be used to restrict the results to specific
edge connections only. The matching is then done via the @LIT{MATCHES} function.
To not restrict the result to specific connections, @FA{edgeexamples} should be left
unspecified.
Example calls:
EDGES(friendrelations, "friends/john", "outbound")
EDGES(friendrelations, "friends/john", "any", [ { "$label": "knows" } ])
- @FN{NEIGHBORS(@FA{vertexcollection}\, @FA{edgecollection}\, @FA{startvertex}\, @FA{direction}, @FA{edgeexamples})}:
return all neighbors that are directly connected to the vertex @FA{startvertex} as a list.
The possible values for @FA{direction} are:
- `outbound`: return all outbound edges
- `inbound`: return all inbound edges
- `any`: return outbound and inbound edges
The @FA{edgeexamples} parameter can optionally be used to restrict the results to specific
edge connections only. The matching is then done via the @LIT{MATCHES} function.
To not restrict the result to specific connections, @FA{edgeexamples} should be left
unspecified.
Example calls:
EDGES(friendrelations, "friends/john", "outgoing")
NEIGHBORS(friends, friendrelations, "friends/john", "outbound")
NEIGHBORS(users, usersrelations, "users/john", "any", [ { "$label": "recommends" } ] )
@subsubsection AqlFunctionsControl Control flow functions

View File

@ -653,7 +653,8 @@ TRI_associative_pointer_t* TRI_InitialiseFunctionsAql (void) {
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("TRAVERSAL_TREE", "GRAPH_TRAVERSAL_TREE", false, false, "h,h,s,s,s,a", NULL);
REGISTER_FUNCTION("EDGES", "GRAPH_EDGES", false, false, "h,s,s", 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);
// misc functions
REGISTER_FUNCTION("FAIL", "FAIL", false, false, "|s", NULL); // FAIL is non-deterministic, otherwise query optimisation will fail!

View File

@ -1,5 +1,5 @@
/*jslint indent: 2, nomen: true, maxlen: 100, sloppy: true, vars: true, white: true, plusplus: true, continue: true */
/*global require, exports, COMPARE_STRING */
/*global require, exports, COMPARE_STRING, MATCHES */
////////////////////////////////////////////////////////////////////////////////
/// @brief Ahuacatl, internal query functions
@ -79,6 +79,29 @@ var TYPEWEIGHT_DOCUMENT = 16;
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief filter using a list of examples
////////////////////////////////////////////////////////////////////////////////
function FILTER (list,
examples) {
var result = [ ], i;
if (examples === undefined || examples === null) {
return list;
}
for (i = 0; i < list.length; ++i) {
var element = list[i];
if (MATCHES(element, examples, false)) {
result.push(element);
}
}
return result;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief throw a runtime exception
////////////////////////////////////////////////////////////////////////////////
@ -2803,6 +2826,7 @@ function MATCHES (element, examples, returnIndex) {
if (! Array.isArray(examples)) {
examples = [ examples ];
}
if (examples.length === 0) {
THROW(INTERNAL.errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH, "MATCHES");
}
@ -3258,7 +3282,8 @@ function GRAPH_TRAVERSAL_TREE (vertexCollection,
function GRAPH_EDGES (edgeCollection,
vertex,
direction) {
direction,
examples) {
var c = COLLECTION(edgeCollection), result;
// validate arguments
@ -3275,6 +3300,55 @@ function GRAPH_EDGES (edgeCollection,
THROW(INTERNAL.errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH, "EDGES");
}
return FILTER(result, examples);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return connected neighbors
////////////////////////////////////////////////////////////////////////////////
function GRAPH_NEIGHBORS (vertexCollection,
edgeCollection,
vertex,
direction,
examples) {
var c = COLLECTION(vertexCollection);
if (vertex.indexOf('/') === -1) {
vertex = vertexCollection + '/' + vertex;
}
var edges = GRAPH_EDGES(edgeCollection, vertex, direction);
var result = [ ];
FILTER(edges, examples).forEach (function (e) {
var key;
if (direction === "inbound") {
key = e._from;
}
else if (direction === "outbound") {
key = e._to;
}
else if (direction === "any") {
key = e._from;
if (key === vertex) {
key = e._to;
}
}
if (key === vertex) {
// do not return the start vertex itself
return;
}
try {
result.push({ edge: CLONE(e), vertex: c.document(key) });
}
catch (err) {
}
});
return result;
}
@ -3446,6 +3520,7 @@ exports.GRAPH_PATHS = GRAPH_PATHS;
exports.GRAPH_TRAVERSAL = GRAPH_TRAVERSAL;
exports.GRAPH_TRAVERSAL_TREE = GRAPH_TRAVERSAL_TREE;
exports.GRAPH_EDGES = GRAPH_EDGES;
exports.GRAPH_NEIGHBORS = GRAPH_NEIGHBORS;
exports.NOT_NULL = NOT_NULL;
exports.FIRST_LIST = FIRST_LIST;
exports.FIRST_DOCUMENT = FIRST_DOCUMENT;

View File

@ -206,6 +206,72 @@ function ahuacatlQueryEdgesTestSuite () {
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks NEIGHBORS()
////////////////////////////////////////////////////////////////////////////////
testNeighborsAny : function () {
var actual;
actual = getQueryResults("FOR n IN NEIGHBORS(UnitTestsAhuacatlVertex, UnitTestsAhuacatlEdge, 'UnitTestsAhuacatlVertex/v1', 'any') SORT n.vertex._key RETURN [ n.vertex._key, n.edge.what ]", true);
assertEqual(actual, [ [ "v2", "v1->v2" ], [ "v3", "v1->v3" ] ]);
actual = getQueryResults("FOR n IN NEIGHBORS(UnitTestsAhuacatlVertex, UnitTestsAhuacatlEdge, 'UnitTestsAhuacatlVertex/v2', 'any') SORT n.vertex._key RETURN [ n.vertex._key, n.edge.what ]", true);
assertEqual(actual, [ [ "v1", "v1->v2" ], [ "v3", "v2->v3" ], [ "v4", "v4->v2" ] ]);
actual = getQueryResults("FOR n IN NEIGHBORS(UnitTestsAhuacatlVertex, UnitTestsAhuacatlEdge, 'UnitTestsAhuacatlVertex/v3', 'any') SORT n.vertex._key, n.edge.what RETURN [ n.vertex._key, n.edge.what ]", true);
assertEqual(actual, [ [ "v1", "v1->v3"], [ "v2", "v2->v3" ], [ "v4", "v3->v4" ], [ "v6", "v3->v6" ], [ "v6", "v6->v3"], [ "v7", "v3->v7" ], [ "v7", "v7->v3" ] ]);
actual = getQueryResults("FOR n IN NEIGHBORS(UnitTestsAhuacatlVertex, UnitTestsAhuacatlEdge, 'UnitTestsAhuacatlVertex/v8', 'any') SORT n.vertex._key RETURN n.vertex._key", true);
assertEqual(actual, [ ]);
actual = getQueryResults("FOR n IN NEIGHBORS(UnitTestsAhuacatlVertex, UnitTestsAhuacatlEdge, 'UnitTestsAhuacatlVertex/v5', 'any') SORT n.vertex._key RETURN n.vertex._key", true);
assertEqual(actual, [ ]);
actual = getQueryResults("FOR n IN NEIGHBORS(UnitTestsAhuacatlVertex, UnitTestsAhuacatlEdge, 'UnitTestsAhuacatlVertex/thefox', 'any') SORT n.vertex._key RETURN n.vertex._key", true);
assertEqual(actual, [ ]);
try {
actual = getQueryResults("FOR n IN NEIGHBORS(UnitTestsAhuacatlVertex, UnitTestsAhuacatlEdge, 'thefox/thefox', 'any') SORT n.vertex._key RETURN n.vertex._key", true);
}
catch (err) {
assertEqual(errors.ERROR_ARANGO_COLLECTION_NOT_FOUND.code, err.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks NEIGHBORS()
////////////////////////////////////////////////////////////////////////////////
testNeighborsIn : function () {
var actual;
actual = getQueryResults("FOR n IN NEIGHBORS(UnitTestsAhuacatlVertex, UnitTestsAhuacatlEdge, 'UnitTestsAhuacatlVertex/v1', 'inbound') SORT n.vertex._key RETURN [ n.vertex._key, n.edge.what ]", true);
assertEqual(actual, [ ]);
actual = getQueryResults("FOR n IN NEIGHBORS(UnitTestsAhuacatlVertex, UnitTestsAhuacatlEdge, 'UnitTestsAhuacatlVertex/v2', 'inbound') SORT n.vertex._key RETURN [ n.vertex._key, n.edge.what ]", true);
assertEqual(actual, [ [ "v1", "v1->v2" ], [ "v4", "v4->v2" ] ]);
actual = getQueryResults("FOR n IN NEIGHBORS(UnitTestsAhuacatlVertex, UnitTestsAhuacatlEdge, 'UnitTestsAhuacatlVertex/v3', 'inbound') SORT n.vertex._key RETURN [ n.vertex._key, n.edge.what ]", true);
assertEqual(actual, [ [ "v1", "v1->v3"], [ "v2", "v2->v3" ], [ "v6", "v6->v3"], [ "v7", "v7->v3" ] ]);
actual = getQueryResults("FOR n IN NEIGHBORS(UnitTestsAhuacatlVertex, UnitTestsAhuacatlEdge, 'UnitTestsAhuacatlVertex/v8', 'inbound') SORT n.vertex._key RETURN n.vertex._key", true);
assertEqual(actual, [ ]);
actual = getQueryResults("FOR n IN NEIGHBORS(UnitTestsAhuacatlVertex, UnitTestsAhuacatlEdge, 'UnitTestsAhuacatlVertex/v5', 'inbound') SORT n.vertex._key RETURN n.vertex._key", true);
assertEqual(actual, [ ]);
actual = getQueryResults("FOR n IN NEIGHBORS(UnitTestsAhuacatlVertex, UnitTestsAhuacatlEdge, 'UnitTestsAhuacatlVertex/thefox', 'inbound') SORT n.vertex._key RETURN n.vertex._key", true);
assertEqual(actual, [ ]);
try {
actual = getQueryResults("FOR n IN NEIGHBORS(UnitTestsAhuacatlVertex, UnitTestsAhuacatlEdge, 'thefox/thefox', 'inbound') SORT n.vertex._key RETURN n.vertex._key", true);
}
catch (err) {
assertEqual(errors.ERROR_ARANGO_COLLECTION_NOT_FOUND.code, err.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks EDGES()
////////////////////////////////////////////////////////////////////////////////
@ -222,9 +288,6 @@ function ahuacatlQueryEdgesTestSuite () {
actual = getQueryResults("FOR e IN EDGES(UnitTestsAhuacatlEdge, 'UnitTestsAhuacatlVertex/v3', 'outbound') SORT e.what RETURN e.what", true);
assertEqual(actual, [ "v3->v4", "v3->v6", "v3->v7" ]);
actual = getQueryResults("FOR e IN EDGES(UnitTestsAhuacatlEdge, 'UnitTestsAhuacatlVertex/v3', 'outbound') SORT e.what RETURN e.what", true);
assertEqual(actual, [ "v3->v4", "v3->v6", "v3->v7" ]);
actual = getQueryResults("FOR e IN EDGES(UnitTestsAhuacatlEdge, 'UnitTestsAhuacatlVertex/v8', 'outbound') SORT e.what RETURN e.what", true);
assertEqual(actual, [ ]);
@ -240,6 +303,39 @@ function ahuacatlQueryEdgesTestSuite () {
catch (err) {
assertEqual(errors.ERROR_ARANGO_COLLECTION_NOT_FOUND.code, err.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief checks NEIGHBORS()
////////////////////////////////////////////////////////////////////////////////
testNeighborsOut : function () {
var actual;
actual = getQueryResults("FOR n IN NEIGHBORS(UnitTestsAhuacatlVertex, UnitTestsAhuacatlEdge, 'UnitTestsAhuacatlVertex/v1', 'outbound') SORT n.vertex._key RETURN [ n.vertex._key, n.edge.what ]", true);
assertEqual(actual, [ [ "v2", "v1->v2" ], [ "v3", "v1->v3" ] ]);
actual = getQueryResults("FOR n IN NEIGHBORS(UnitTestsAhuacatlVertex, UnitTestsAhuacatlEdge, 'UnitTestsAhuacatlVertex/v2', 'outbound') SORT n.vertex._key RETURN [ n.vertex._key, n.edge.what ]", true);
assertEqual(actual, [ [ "v3", "v2->v3" ] ]);
actual = getQueryResults("FOR n IN NEIGHBORS(UnitTestsAhuacatlVertex, UnitTestsAhuacatlEdge, 'UnitTestsAhuacatlVertex/v3', 'outbound') SORT n.vertex._key RETURN n.vertex._key", true);
assertEqual(actual, [ "v4", "v6", "v7" ]);
actual = getQueryResults("FOR n IN NEIGHBORS(UnitTestsAhuacatlVertex, UnitTestsAhuacatlEdge, 'UnitTestsAhuacatlVertex/v8', 'outbound') SORT n.vertex._key RETURN n.vertex._key", true);
assertEqual(actual, [ ]);
actual = getQueryResults("FOR n IN NEIGHBORS(UnitTestsAhuacatlVertex, UnitTestsAhuacatlEdge, 'UnitTestsAhuacatlVertex/v5', 'outbound') SORT n.vertex._key RETURN n.vertex._key", true);
assertEqual(actual, [ ]);
actual = getQueryResults("FOR n IN NEIGHBORS(UnitTestsAhuacatlVertex, UnitTestsAhuacatlEdge, 'UnitTestsAhuacatlVertex/thefox', 'outbound') SORT n.vertex._key RETURN n.vertex._key", true);
assertEqual(actual, [ ]);
try {
actual = getQueryResults("FOR n IN NEIGHBORS(UnitTestsAhuacatlVertex, UnitTestsAhuacatlEdge, 'thefox/thefox', 'outbound') SORT n.vertex._key RETURN n.vertex._key", true);
}
catch (err) {
assertEqual(errors.ERROR_ARANGO_COLLECTION_NOT_FOUND.code, err.errorNum);
}
}
};