mirror of https://gitee.com/bigwinds/arangodb
Merge branch 'devel' of github.com:triAGENS/ArangoDB into devel
This commit is contained in:
commit
bad4c293ad
|
@ -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
|
/// @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;
|
TRI_aql_node_t* direction;
|
||||||
char* directionValue;
|
char* directionValue;
|
||||||
char* name;
|
char* name;
|
||||||
const char* lookFor;
|
|
||||||
size_t len;
|
|
||||||
size_t n;
|
size_t n;
|
||||||
|
|
||||||
args = TRI_AQL_NODE_MEMBER(fcallNode, 0);
|
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
|
// try to optimise the vertex collection access
|
||||||
if (TRI_EqualString(directionValue, "outbound")) {
|
if (TRI_EqualString(directionValue, "outbound")) {
|
||||||
lookFor = ".source.";
|
CheckPathRestriction(fieldAccess, context, vertexCollection, ".source.", name, n);
|
||||||
len = strlen(lookFor);
|
|
||||||
}
|
}
|
||||||
else if (TRI_EqualString(directionValue, "inbound")) {
|
else if (TRI_EqualString(directionValue, "inbound")) {
|
||||||
lookFor = ".destination.";
|
CheckPathRestriction(fieldAccess, context, vertexCollection, ".destination.", name, n);
|
||||||
len = strlen(lookFor);
|
|
||||||
}
|
}
|
||||||
else {
|
else if (TRI_EqualString(directionValue, "any")) {
|
||||||
// "any" will not be optimised
|
CheckPathRestriction(fieldAccess, context, vertexCollection, ".source.", name, n);
|
||||||
lookFor = NULL;
|
CheckPathRestriction(fieldAccess, context, vertexCollection, ".destination.", name, n);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if we have a filter on LENGTH(edges)
|
||||||
if (args->_members._length <= 4 &&
|
if (args->_members._length <= 4 &&
|
||||||
TRI_EqualString(name, ".edges.LENGTH()")) {
|
TRI_EqualString(name, ".edges.LENGTH()")) {
|
||||||
// length restriction, can only be applied if length parameters are not already set
|
// 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
|
// 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("TRAVERSE", "GRAPH_TRAVERSE", false, false, "h,h,s,s,a", NULL);
|
||||||
|
|
||||||
// misc functions
|
// misc functions
|
||||||
REGISTER_FUNCTION("FAIL", "FAIL", false, false, "|s", NULL); // FAIL is non-deterministic, otherwise query optimisation will fail!
|
REGISTER_FUNCTION("FAIL", "FAIL", false, false, "|s", NULL); // FAIL is non-deterministic, otherwise query optimisation will fail!
|
||||||
|
|
|
@ -678,10 +678,10 @@ static TRI_datafile_t* OpenDatafile (char const* filename, bool ignoreErrors) {
|
||||||
|
|
||||||
// check the maximal size
|
// check the maximal size
|
||||||
if (size > header._maximalSize) {
|
if (size > header._maximalSize) {
|
||||||
LOG_WARNING("datafile '%s' has size '%u', but maximal size is '%u'",
|
LOG_DEBUG("datafile '%s' has size '%u', but maximal size is '%u'",
|
||||||
filename,
|
filename,
|
||||||
(unsigned int) size,
|
(unsigned int) size,
|
||||||
(unsigned int) header._maximalSize);
|
(unsigned int) header._maximalSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// map datafile into memory
|
// map datafile into memory
|
||||||
|
|
|
@ -430,8 +430,13 @@ function CollectionOutboundExpander (config, vertex, path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
outEdges.forEach(function (edge) {
|
outEdges.forEach(function (edge) {
|
||||||
var vertex = internal.db._document(edge._to);
|
try {
|
||||||
connections.push({ edge: edge, vertex: vertex });
|
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;
|
return connections;
|
||||||
|
@ -450,8 +455,13 @@ function CollectionInboundExpander (config, vertex, path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
inEdges.forEach(function (edge) {
|
inEdges.forEach(function (edge) {
|
||||||
var vertex = internal.db._document(edge._from);
|
try {
|
||||||
connections.push({ edge: edge, vertex: vertex });
|
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;
|
return connections;
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
var internal = require("internal");
|
var internal = require("internal");
|
||||||
|
var traversal = require("org/arangodb/graph/traversal");
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief type weight used for sorting and comparing
|
/// @brief type weight used for sorting and comparing
|
||||||
|
@ -2209,8 +2210,6 @@ function AHUACATL_GRAPH_PATHS () {
|
||||||
followCycles : followCycles
|
followCycles : followCycles
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: restrict allEdges to edges with certain _from values etc.
|
|
||||||
|
|
||||||
var result = [ ];
|
var result = [ ];
|
||||||
var n = vertices.length;
|
var n = vertices.length;
|
||||||
for (var i = 0; i < n; ++i) {
|
for (var i = 0; i < n; ++i) {
|
||||||
|
@ -2304,6 +2303,96 @@ function AHUACATL_GRAPH_SUBNODES (searchAttributes, vertexId, visited, edges, ve
|
||||||
return result;
|
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 ];
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @}
|
/// @}
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
Loading…
Reference in New Issue