mirror of https://gitee.com/bigwinds/arangodb
Enhanced pathTo for directed and labelled graphs
The `pathTo` method accepts an options hash. Currently supported: * direction: "both", "outbound", "inbound" * labels: undefined or Array of Strings
This commit is contained in:
parent
70ae8d84d4
commit
f11acff837
|
@ -80,6 +80,18 @@ Array.prototype.pushIfNotFound = function (element) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief add all elements of the other array, that are not already in here
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
Array.prototype.merge = function (other_array) {
|
||||||
|
var i;
|
||||||
|
|
||||||
|
for (i = 0; i < other_array.length; i += 1) {
|
||||||
|
this.pushIfNotFound(other_array[i]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief shallow copy properties
|
/// @brief shallow copy properties
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -773,25 +785,23 @@ Vertex.prototype.setProperty = function (name, value) {
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief find the shortest path to a certain vertex, return the ID
|
/// @brief find the shortest path to a certain vertex, return the ID
|
||||||
///
|
///
|
||||||
/// @FUN{@FA{vertex}.pathTo(@FA{target_vertex})}
|
/// @FUN{@FA{vertex}.pathTo(@FA{target_vertex}, @FA{options})}
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
Vertex.prototype.pathTo = function (target_vertex) {
|
Vertex.prototype.pathTo = function (target_vertex, options) {
|
||||||
var predecessors = {}; // { ID => [ID] }
|
var predecessors = target_vertex.determinePredecessors(this.getId(), options || {});
|
||||||
|
|
||||||
predecessors = target_vertex.determinePredecessors(this.getId());
|
|
||||||
return target_vertex.pathesForTree(predecessors);
|
return target_vertex.pathesForTree(predecessors);
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief Helper function for pathTo
|
/// @brief determine all the pathes to this node from source id
|
||||||
///
|
///
|
||||||
/// @FUN{@FA{vertex}.determinePredecessors(@FA{source_id})}
|
/// @FUN{@FA{vertex}.determinePredecessors(@FA{source_id}, @FA{options})}
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
Vertex.prototype.determinePredecessors = function (source_id) {
|
Vertex.prototype.determinePredecessors = function (source_id, options) {
|
||||||
var determined_list = [], // [ID]
|
var determined_list = [], // [ID]
|
||||||
predecessors = {}, // { ID => [ID] }
|
predecessors = {}, // { ID => [ID] }
|
||||||
todo_list = [source_id], // [ID]
|
todo_list = [source_id], // [ID]
|
||||||
|
@ -803,43 +813,54 @@ Vertex.prototype.determinePredecessors = function (source_id) {
|
||||||
|
|
||||||
while (todo_list.length > 0) {
|
while (todo_list.length > 0) {
|
||||||
current_vertex_id = this._getShortestDistance(todo_list, distances);
|
current_vertex_id = this._getShortestDistance(todo_list, distances);
|
||||||
|
current_vertex = this._graph.getVertex(current_vertex_id);
|
||||||
|
|
||||||
if (current_vertex_id === this.getId()) {
|
if (current_vertex_id === this.getId()) {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
todo_list.removeLastOccurrenceOf(current_vertex_id);
|
todo_list.removeLastOccurrenceOf(current_vertex_id);
|
||||||
determined_list.push(current_vertex_id);
|
determined_list.push(current_vertex_id);
|
||||||
current_vertex = this._graph.getVertex(current_vertex_id);
|
|
||||||
current_vertex.processNeighbors(
|
todo_list.merge(current_vertex._processNeighbors(
|
||||||
determined_list,
|
determined_list,
|
||||||
todo_list,
|
|
||||||
distances,
|
distances,
|
||||||
predecessors
|
predecessors,
|
||||||
);
|
options
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return predecessors;
|
return predecessors;
|
||||||
};
|
};
|
||||||
|
|
||||||
Vertex.prototype.processNeighbors = function (determined_list, todo_list, distances, predecessors) {
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief Helper function for determinePredecessors (changes distance and predecessors
|
||||||
|
///
|
||||||
|
/// @FUN{@FA{vertex}._processNeighbors(@FA{determinedt}, @FA{distances}, @FA{predecessors})}
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
Vertex.prototype._processNeighbors = function (determined_list, distances, predecessors, options) {
|
||||||
var i,
|
var i,
|
||||||
current_neighbor_id,
|
current_neighbor_id,
|
||||||
current_distance,
|
current_distance,
|
||||||
raw_neighborlist,
|
raw_neighborlist,
|
||||||
compared_distance;
|
compared_distance,
|
||||||
|
not_determined_neighbors = [];
|
||||||
|
|
||||||
// FIXME: Choose the neighbors according to the graph (directed/undirected)
|
// FIXME: Choose the neighbors according to labels
|
||||||
raw_neighborlist = this.getNeighbors('both');
|
raw_neighborlist = this.getNeighbors(options.direction || 'both', options.labels);
|
||||||
|
// console.log("Processing Neighbors for: " + this.getId());
|
||||||
|
// console.log(raw_neighborlist);
|
||||||
|
|
||||||
for (i = 0; i < raw_neighborlist.length; i += 1) {
|
for (i = 0; i < raw_neighborlist.length; i += 1) {
|
||||||
current_neighbor_id = raw_neighborlist[i];
|
current_neighbor_id = raw_neighborlist[i];
|
||||||
|
|
||||||
if (determined_list.lastIndexOf(current_neighbor_id) === -1) {
|
if (determined_list.lastIndexOf(current_neighbor_id) === -1) {
|
||||||
// FIXME: Take the weight if it is a weighted graph
|
// FIXME: Weights other than 1
|
||||||
current_distance = distances[this.getId()] + 1;
|
current_distance = distances[this.getId()] + 1;
|
||||||
|
|
||||||
todo_list.pushIfNotFound(current_neighbor_id);
|
not_determined_neighbors.push(current_neighbor_id);
|
||||||
|
|
||||||
compared_distance = distances[current_neighbor_id];
|
compared_distance = distances[current_neighbor_id];
|
||||||
if ((compared_distance === undefined) || (compared_distance > current_distance)) {
|
if ((compared_distance === undefined) || (compared_distance > current_distance)) {
|
||||||
|
@ -850,6 +871,8 @@ Vertex.prototype.processNeighbors = function (determined_list, todo_list, distan
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return not_determined_neighbors;
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -152,7 +152,7 @@ function dijkstraSuite() {
|
||||||
graph.addEdge(v1, v2, 8, 'a');
|
graph.addEdge(v1, v2, 8, 'a');
|
||||||
graph.addEdge(v1, v3, 9, 'b');
|
graph.addEdge(v1, v3, 9, 'b');
|
||||||
|
|
||||||
result_array = v1.getNeighbors('both', ['a']);
|
result_array = v1.getNeighbors('both', ['a', 'c']);
|
||||||
|
|
||||||
assertEqual(result_array.length, 1);
|
assertEqual(result_array.length, 1);
|
||||||
assertEqual(result_array[0], 2);
|
assertEqual(result_array[0], 2);
|
||||||
|
@ -218,7 +218,72 @@ function dijkstraSuite() {
|
||||||
assertEqual(path[0].toString(), v1.getId());
|
assertEqual(path[0].toString(), v1.getId());
|
||||||
assertEqual(path[1].toString(), v2.getId());
|
assertEqual(path[1].toString(), v2.getId());
|
||||||
assertEqual(path[2].toString(), v3.getId());
|
assertEqual(path[2].toString(), v3.getId());
|
||||||
}
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief get a short, distinct path on a directed graph
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testGetADirectedDistinctPath : function () {
|
||||||
|
var v1 = graph.addVertex(1),
|
||||||
|
v2 = graph.addVertex(2),
|
||||||
|
v3 = graph.addVertex(3),
|
||||||
|
v4 = graph.addVertex(4),
|
||||||
|
e1 = graph.addEdge(v1, v2),
|
||||||
|
e2 = graph.addEdge(v2, v3),
|
||||||
|
e3 = graph.addEdge(v3, v4),
|
||||||
|
e4 = graph.addEdge(v4, v1),
|
||||||
|
pathes = v1.pathTo(v3, {direction: "outbound"});
|
||||||
|
|
||||||
|
assertEqual(pathes.length, 1);
|
||||||
|
assertEqual(pathes[0][0].toString(), v1.getId());
|
||||||
|
assertEqual(pathes[0][1].toString(), v2.getId());
|
||||||
|
assertEqual(pathes[0][2].toString(), v3.getId());
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief get a short, distinct path on a directed graph
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testGetADirectedLabeledPath : function () {
|
||||||
|
var v1 = graph.addVertex(1),
|
||||||
|
v2 = graph.addVertex(2),
|
||||||
|
v3 = graph.addVertex(3),
|
||||||
|
v4 = graph.addVertex(4),
|
||||||
|
e1 = graph.addEdge(v1, v2, 5, "no"),
|
||||||
|
e2 = graph.addEdge(v1, v3, 6, "yes"),
|
||||||
|
e3 = graph.addEdge(v3, v4, 7, "yeah"),
|
||||||
|
e4 = graph.addEdge(v4, v2, 8, "yes"),
|
||||||
|
pathes = v1.pathTo(v2, { labels: ["yes", "yeah"] });
|
||||||
|
|
||||||
|
assertEqual(pathes.length, 1);
|
||||||
|
assertEqual(pathes[0][0].toString(), v1.getId());
|
||||||
|
assertEqual(pathes[0][1].toString(), v3.getId());
|
||||||
|
assertEqual(pathes[0][2].toString(), v4.getId());
|
||||||
|
assertEqual(pathes[0][3].toString(), v2.getId());
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief get a short, distinct path on a weighted graph
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/*testGetADirectedWeightedPath : function () {
|
||||||
|
var v1 = graph.addVertex(1),
|
||||||
|
v2 = graph.addVertex(2),
|
||||||
|
v3 = graph.addVertex(3),
|
||||||
|
v4 = graph.addVertex(4),
|
||||||
|
e1 = graph.addEdge(v1, v2, 5, "5", { my_weight: 5 }),
|
||||||
|
e2 = graph.addEdge(v1, v3, 6, "6", { my_weight: 1 }),
|
||||||
|
e3 = graph.addEdge(v3, v4, 7, "7", { my_weight: 1 }),
|
||||||
|
e4 = graph.addEdge(v4, v1, 8, "8", { my_weight: 1 }),
|
||||||
|
pathes = v1.pathTo(v2, { weight: "my_weight" });
|
||||||
|
|
||||||
|
assertEqual(pathes.length, 1);
|
||||||
|
assertEqual(pathes[0][0].toString(), v1.getId());
|
||||||
|
assertEqual(pathes[0][1].toString(), v3.getId());
|
||||||
|
assertEqual(pathes[0][2].toString(), v4.getId());
|
||||||
|
assertEqual(pathes[0][3].toString(), v2.getId());
|
||||||
|
}*/
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue