From 5f2a6c803b725d1550a9ef841b4c94b4fadaf116 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Sun, 13 Jan 2013 13:18:57 +0100 Subject: [PATCH 1/2] do not show irrelevant warning --- arangod/VocBase/datafile.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arangod/VocBase/datafile.c b/arangod/VocBase/datafile.c index 223347b251..07ce955f2b 100644 --- a/arangod/VocBase/datafile.c +++ b/arangod/VocBase/datafile.c @@ -678,10 +678,10 @@ static TRI_datafile_t* OpenDatafile (char const* filename, bool ignoreErrors) { // check the maximal size if (size > header._maximalSize) { - LOG_WARNING("datafile '%s' has size '%u', but maximal size is '%u'", - filename, - (unsigned int) size, - (unsigned int) header._maximalSize); + LOG_DEBUG("datafile '%s' has size '%u', but maximal size is '%u'", + filename, + (unsigned int) size, + (unsigned int) header._maximalSize); } // map datafile into memory From 8bf8a733a2db51e73bd2b1bf4c58de1801c692cf Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Sun, 13 Jan 2013 13:48:23 +0100 Subject: [PATCH 2/2] added traversal function --- arangod/Ahuacatl/ahuacatl-functions.c | 78 ++++++++++------ .../modules/org/arangodb/graph/traversal.js | 18 +++- js/server/ahuacatl.js | 93 ++++++++++++++++++- 3 files changed, 155 insertions(+), 34 deletions(-) diff --git a/arangod/Ahuacatl/ahuacatl-functions.c b/arangod/Ahuacatl/ahuacatl-functions.c index 1424e2dcfa..317375842b 100644 --- a/arangod/Ahuacatl/ahuacatl-functions.c +++ b/arangod/Ahuacatl/ahuacatl-functions.c @@ -327,6 +327,49 @@ static bool EqualName (TRI_associative_pointer_t* array, /// @{ //////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// @brief check if we have a matching restriction we can use to optimise +/// a PATHS query +//////////////////////////////////////////////////////////////////////////////// + +static bool CheckPathRestriction (TRI_aql_field_access_t* fieldAccess, + TRI_aql_context_t* const context, + TRI_aql_node_t* vertexCollection, + const char* lookFor, + char* name, + const size_t n) { + size_t len; + + assert(fieldAccess); + assert(lookFor); + + len = strlen(lookFor); + if (len == 0) { + return false; + } + + if (n > fieldAccess->_variableNameLength + len && + memcmp((void*) lookFor, (void*) name, len) == 0) { + // we'll now patch the collection hint + TRI_aql_collection_hint_t* hint; + + // field name is collection.source.XXX, e.g. users.source._id + LOG_DEBUG("optimising PATHS() field access %s", fieldAccess->_fullName); + + // we can now modify this fieldaccess in place to collection.XXX, e.g. users._id + // copy trailing \0 byte as well + memmove(name, name + len - 1, n - fieldAccess->_variableNameLength - len + 2); + + // attach the modified fieldaccess to the collection + hint = (TRI_aql_collection_hint_t*) (TRI_AQL_NODE_DATA(vertexCollection)); + hint->_ranges = TRI_AddAccessAql(context, hint->_ranges, fieldAccess); + + return true; + } + + return false; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief optimise callback function for PATHS() AQL function //////////////////////////////////////////////////////////////////////////////// @@ -340,8 +383,6 @@ static void OptimisePaths (const TRI_aql_node_t* const fcallNode, TRI_aql_node_t* direction; char* directionValue; char* name; - const char* lookFor; - size_t len; size_t n; args = TRI_AQL_NODE_MEMBER(fcallNode, 0); @@ -366,37 +407,17 @@ static void OptimisePaths (const TRI_aql_node_t* const fcallNode, // try to optimise the vertex collection access if (TRI_EqualString(directionValue, "outbound")) { - lookFor = ".source."; - len = strlen(lookFor); + CheckPathRestriction(fieldAccess, context, vertexCollection, ".source.", name, n); } else if (TRI_EqualString(directionValue, "inbound")) { - lookFor = ".destination."; - len = strlen(lookFor); + CheckPathRestriction(fieldAccess, context, vertexCollection, ".destination.", name, n); } - else { - // "any" will not be optimised - lookFor = NULL; - len = 0; - } - - if (len > 0 && - n > fieldAccess->_variableNameLength + len && - memcmp((void*) lookFor, (void*) name, len) == 0) { - // we'll now patch the collection hint - TRI_aql_collection_hint_t* hint; - - // field name is collection.source.XXX, e.g. users.source._id - LOG_DEBUG("optimising PATHS() field access %s", fieldAccess->_fullName); - - // we can now modify this fieldaccess in place to collection.XXX, e.g. users._id - // copy trailing \0 byte as well - memmove(name, name + len - 1, n - fieldAccess->_variableNameLength - len + 2); - - // attach the modified fieldaccess to the collection - hint = (TRI_aql_collection_hint_t*) (TRI_AQL_NODE_DATA(vertexCollection)); - hint->_ranges = TRI_AddAccessAql(context, hint->_ranges, fieldAccess); + else if (TRI_EqualString(directionValue, "any")) { + CheckPathRestriction(fieldAccess, context, vertexCollection, ".source.", name, n); + CheckPathRestriction(fieldAccess, context, vertexCollection, ".destination.", name, n); } + // check if we have a filter on LENGTH(edges) if (args->_members._length <= 4 && TRI_EqualString(name, ".edges.LENGTH()")) { // length restriction, can only be applied if length parameters are not already set @@ -603,6 +624,7 @@ TRI_associative_pointer_t* TRI_InitialiseFunctionsAql (void) { // graph functions REGISTER_FUNCTION("PATHS", "GRAPH_PATHS", false, false, "c,h|s,b", &OptimisePaths); + REGISTER_FUNCTION("TRAVERSE", "GRAPH_TRAVERSE", false, false, "h,h,s,s,a", NULL); // misc functions REGISTER_FUNCTION("FAIL", "FAIL", false, false, "|s", NULL); // FAIL is non-deterministic, otherwise query optimisation will fail! diff --git a/js/common/modules/org/arangodb/graph/traversal.js b/js/common/modules/org/arangodb/graph/traversal.js index 98bbf50fec..e68c75a6ab 100644 --- a/js/common/modules/org/arangodb/graph/traversal.js +++ b/js/common/modules/org/arangodb/graph/traversal.js @@ -430,8 +430,13 @@ function CollectionOutboundExpander (config, vertex, path) { } outEdges.forEach(function (edge) { - var vertex = internal.db._document(edge._to); - connections.push({ edge: edge, vertex: vertex }); + try { + var vertex = internal.db._document(edge._to); + connections.push({ edge: edge, vertex: vertex }); + } + catch (e) { + // continue even in the face of non-existing documents + } }); return connections; @@ -450,8 +455,13 @@ function CollectionInboundExpander (config, vertex, path) { } inEdges.forEach(function (edge) { - var vertex = internal.db._document(edge._from); - connections.push({ edge: edge, vertex: vertex }); + try { + var vertex = internal.db._document(edge._from); + connections.push({ edge: edge, vertex: vertex }); + } + catch (e) { + // continue even in the face of non-existing documents + } }); return connections; diff --git a/js/server/ahuacatl.js b/js/server/ahuacatl.js index b56fed480f..361291c258 100644 --- a/js/server/ahuacatl.js +++ b/js/server/ahuacatl.js @@ -26,6 +26,7 @@ //////////////////////////////////////////////////////////////////////////////// var internal = require("internal"); +var traversal = require("org/arangodb/graph/traversal"); //////////////////////////////////////////////////////////////////////////////// /// @brief type weight used for sorting and comparing @@ -2209,8 +2210,6 @@ function AHUACATL_GRAPH_PATHS () { followCycles : followCycles }; - // TODO: restrict allEdges to edges with certain _from values etc. - var result = [ ]; var n = vertices.length; for (var i = 0; i < n; ++i) { @@ -2304,6 +2303,96 @@ function AHUACATL_GRAPH_SUBNODES (searchAttributes, vertexId, visited, edges, ve return result; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief visitor callback function for traversal +//////////////////////////////////////////////////////////////////////////////// + +function AHUACATL_TRAVERSE_VISITOR (config, result, vertex, path) { + result.vertices.push(AHUACATL_CLONE(vertex)); + if (config.trackPaths) { + result.paths.push(AHUACATL_CLONE(path)); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief filter callback function for traversal +//////////////////////////////////////////////////////////////////////////////// + +function AHUACATL_TRAVERSE_FILTER (config, vertex, path) { + if (config.maxDepth != null && config.maxDepth != undefined && path.edges.length > config.maxDepth) { + return "prune"; + } + return ""; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief traverse a graph +//////////////////////////////////////////////////////////////////////////////// + +function AHUACATL_GRAPH_TRAVERSE () { + var vertexCollection = AHUACATL_COLLECTION(arguments[0]); + var edgeCollection = AHUACATL_COLLECTION(arguments[1]); + var startVertex = arguments[2]; + var direction = arguments[3]; + var params = arguments[4]; + + function validate (value, map) { + if (value == null || value == undefined) { + // use first key from map + for (var m in map) { + if (map.hasOwnProperty(m)) { + value = m; + break; + } + } + } + if (typeof value === 'string') { + value = value.toLowerCase().replace(/-/, ""); + if (map[value] != null) { + return map[value]; + } + } + + AHUACATL_THROW(internal.errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH, "TRAVERSE"); + } + + var config = { + edgeCollection: edgeCollection, + strategy: validate(params.strategy, { + 'depthfirst': traversal.Traverser.DEPTH_FIRST, + 'breadthfirst': traversal.Traverser.BREADTH_FIRST + }), + order: validate(params.order, { + 'preorder': traversal.Traverser.PRE_ORDER, + 'postorder': traversal.Traverser.POST_ORDER + }), + itemOrder: validate(params.itemOrder, { + 'forward': traversal.Traverser.FORWARD, + 'backward': traversal.Traverser.BACKWARD + }), + maxDepth: params.maxDepth, + trackPaths: params.returnPaths || false, + visitor: AHUACATL_TRAVERSE_VISITOR, + filter: AHUACATL_TRAVERSE_FILTER, + expander: validate(direction, { + 'outbound': traversal.CollectionOutboundExpander, + 'inbound': traversal.CollectionInboundExpander + }) + }; + + var result = { + vertices: [ ] + }; + if (config.trackPaths) { + result.paths = [ ]; + } + + var traverser = new traversal.Traverser(config); + traverser.traverse(result, vertexCollection.document(startVertex)); + + return [ result ]; +} + //////////////////////////////////////////////////////////////////////////////// /// @} ////////////////////////////////////////////////////////////////////////////////