diff --git a/Documentation/Books/Users/Aql/GraphFunctions.mdpp b/Documentation/Books/Users/Aql/GraphFunctions.mdpp index dc85758c58..ad538a6b17 100644 --- a/Documentation/Books/Users/Aql/GraphFunctions.mdpp +++ b/Documentation/Books/Users/Aql/GraphFunctions.mdpp @@ -144,6 +144,21 @@ instead. Note: The *connections* function parameter value will contain the edges connected to the vertex only if *order* was set to *preorder-expander*. Otherwise, the value of this parameter will be *undefined*. + + The following custom visitor functions are predefined and can be used by specifying the function + name in the *visitor* attribute: + + - *"_AQL::PROJECTINGVISITOR"*: this visitor will produce an object with the attributes + specified in *data.attributes* for each visited vertex. This can be used to create a + projection of each visited vertex' document. + + - *"_AQL::IDVISITOR"*: this visitor will return the _id attribute of each visited vertex. + + - *"_AQL::KEYVISITOR"*: this visitor will return the _key attribute of each visited vertex. + + - *"_AQL::COUNTINGVISITOR"*: this visitor will return a single number indicating the number + of vertices visited. + - *visitorReturnsResults*: only useful in combination with a custom AQL visitor function. If set to *true*, the data returned by the visitor will be appended to the result. If set to @@ -151,6 +166,11 @@ instead. function can modify its *result* parameter value in-place. At the end of the traversal, *result* is expected to be an array. + - *data*: only useful in combination with a custom AQL visitor function. This attribute can + be used to pass arbitrary data into the custom visitor function. The value contained in the + *data* attribute will be made available to the *visitor* function in the *config.data* + attribute. + By default, the result of the TRAVERSAL function is an array of traversed points. Each point is an object consisting of the following attributes: - *vertex*: The vertex at the traversal point diff --git a/js/server/modules/org/arangodb/aql.js b/js/server/modules/org/arangodb/aql.js index ffe1c30845..1a5a746b09 100644 --- a/js/server/modules/org/arangodb/aql.js +++ b/js/server/modules/org/arangodb/aql.js @@ -51,6 +51,46 @@ var RegexCache = { }; var UserFunctions = { }; +//////////////////////////////////////////////////////////////////////////////// +/// @brief prefab traversal visitors +//////////////////////////////////////////////////////////////////////////////// + +var DefaultVisitors = { + "_AQL::PROJECTINGVISITOR" : { + visitorReturnsResults: true, + func: function (config, result, vertex) { + var values = { }; + if (typeof config.data === "object" && Array.isArray(config.data.attributes)) { + config.data.attributes.forEach(function (attribute) { + values[attribute] = vertex[attribute]; + }); + } + return values; + } + }, + "_AQL::IDVISITOR" : { + visitorReturnsResults: true, + func: function (config, result, vertex) { + return vertex._id; + } + }, + "_AQL::KEYVISITOR" : { + visitorReturnsResults: true, + func: function (config, result, vertex) { + return vertex._key; + } + }, + "_AQL::COUNTINGVISITOR" : { + visitorReturnsResults: false, + func: function (config, result) { + if (result.length === 0) { + result.push(0); + } + result[0]++; + } + } +}; + //////////////////////////////////////////////////////////////////////////////// /// @brief type weight used for sorting and comparing //////////////////////////////////////////////////////////////////////////////// @@ -194,25 +234,34 @@ function reloadUserFunctions () { /// @brief get a user-function by name //////////////////////////////////////////////////////////////////////////////// -function GET_USERFUNCTION (name) { +function GET_USERFUNCTION (name, config) { var prefix = DB_PREFIX(), reloaded = false; var key = name.toUpperCase(); - if (! UserFunctions.hasOwnProperty(prefix)) { - reloadUserFunctions(); - reloaded = true; - } - - if (! UserFunctions[prefix].hasOwnProperty(key) && ! reloaded) { - // last chance - reloadUserFunctions(); - } - - if (! UserFunctions[prefix].hasOwnProperty(key)) { - THROW(null, INTERNAL.errors.ERROR_QUERY_FUNCTION_NOT_FOUND, name); - } + var func; - var func = UserFunctions[prefix][key].func; + if (DefaultVisitors.hasOwnProperty(key)) { + var visitor = DefaultVisitors[key]; + func = visitor.func; + config.visitorReturnsResults = visitor.visitorReturnsResults; + } + else { + if (! UserFunctions.hasOwnProperty(prefix)) { + reloadUserFunctions(); + reloaded = true; + } + + if (! UserFunctions[prefix].hasOwnProperty(key) && ! reloaded) { + // last chance + reloadUserFunctions(); + } + + if (! UserFunctions[prefix].hasOwnProperty(key)) { + THROW(null, INTERNAL.errors.ERROR_QUERY_FUNCTION_NOT_FOUND, name); + } + + func = UserFunctions[prefix][key].func; + } if (typeof func !== "function") { THROW(null, INTERNAL.errors.ERROR_QUERY_FUNCTION_NOT_FOUND, name); @@ -225,8 +274,8 @@ function GET_USERFUNCTION (name) { /// @brief create a user-defined visitor from a function name //////////////////////////////////////////////////////////////////////////////// -function GET_VISITOR (name) { - var func = GET_USERFUNCTION(name); +function GET_VISITOR (name, config) { + var func = GET_USERFUNCTION(name, config); return function (config, result, vertex, path) { try { @@ -250,8 +299,8 @@ function GET_VISITOR (name) { /// @brief create a user-defined filter from a function name //////////////////////////////////////////////////////////////////////////////// -function GET_FILTER (name) { - var func = GET_USERFUNCTION(name); +function GET_FILTER (name, config) { + var func = GET_USERFUNCTION(name, config); return function (config, vertex, path) { try { @@ -5059,7 +5108,8 @@ function TRAVERSAL_FUNC (func, endVertex : endVertex, weight : params.weight, defaultWeight : params.defaultWeight, - prefill : params.prefill + prefill : params.prefill, + data: params.data }; if (typeof params.filter === "function") { @@ -5456,7 +5506,7 @@ function SHORTEST_PATH_PARAMS (params) { // add user-defined visitor, if specified if (typeof params.visitor === "string") { - params.visitor = GET_VISITOR(params.visitor); + params.visitor = GET_VISITOR(params.visitor, params); } else { params.visitor = TRAVERSAL_VISITOR; @@ -5464,7 +5514,7 @@ function SHORTEST_PATH_PARAMS (params) { // add user-defined filter, if specified if (typeof params.filter === "string") { - params.filter = GET_FILTER(params.filter); + params.filter = GET_FILTER(params.filter, params); } if (typeof params.distance === "string") { @@ -5686,7 +5736,7 @@ function TRAVERSAL_PARAMS (params) { // add user-defined visitor, if specified if (typeof params.visitor === "string") { - params.visitor = GET_VISITOR(params.visitor); + params.visitor = GET_VISITOR(params.visitor, params); } else { params.visitor = TRAVERSAL_VISITOR; @@ -5694,7 +5744,7 @@ function TRAVERSAL_PARAMS (params) { // add user-defined filter, if specified if (typeof params.filter === "string") { - params.filter = GET_FILTER(params.filter); + params.filter = GET_FILTER(params.filter, params); } return params; @@ -6045,7 +6095,7 @@ function TRAVERSAL_TREE_PARAMS (params, connectName, func) { // add user-defined visitor, if specified if (typeof params.visitor === "string") { - params.visitor = GET_VISITOR(params.visitor); + params.visitor = GET_VISITOR(params.visitor, params); } else { params.visitor = TRAVERSAL_TREE_VISITOR; @@ -6053,7 +6103,7 @@ function TRAVERSAL_TREE_PARAMS (params, connectName, func) { // add user-defined filter, if specified if (typeof params.filter === "string") { - params.filter = GET_FILTER(params.filter); + params.filter = GET_FILTER(params.filter, params); } params.connect = AQL_TO_STRING(connectName);