mirror of https://gitee.com/bigwinds/arangodb
715 lines
20 KiB
JavaScript
715 lines
20 KiB
JavaScript
/*jshint strict: false, unused: false */
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Graph functionality
|
|
///
|
|
/// @file
|
|
///
|
|
/// DISCLAIMER
|
|
///
|
|
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
|
|
///
|
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|
/// you may not use this file except in compliance with the License.
|
|
/// You may obtain a copy of the License at
|
|
///
|
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
|
///
|
|
/// Unless required by applicable law or agreed to in writing, software
|
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
/// See the License for the specific language governing permissions and
|
|
/// limitations under the License.
|
|
///
|
|
/// Copyright holder is triAGENS GmbH, Cologne, Germany
|
|
///
|
|
/// @author Dr. Frank Celler, Lucas Dohmen
|
|
/// @author Copyright 2011-2012, triAGENS GmbH, Cologne, Germany
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
var arangodb = require("@arangodb"),
|
|
is = require("@arangodb/is"),
|
|
shallowCopy = require("@arangodb/util").shallowCopy,
|
|
db = arangodb.db,
|
|
ArangoCollection = arangodb.ArangoCollection,
|
|
common = require("@arangodb/graph-common"),
|
|
newGraph = require("@arangodb/general-graph"),
|
|
Edge = common.Edge,
|
|
Graph = common.Graph,
|
|
Vertex = common.Vertex,
|
|
GraphArray = common.GraphArray,
|
|
Iterator = common.Iterator;
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief find or create a collection by name
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
var findOrCreateCollectionByName = function (name) {
|
|
var col = db._collection(name);
|
|
|
|
if (col === null) {
|
|
col = db._create(name);
|
|
} else if (!(col instanceof ArangoCollection) || col.type() !== ArangoCollection.TYPE_DOCUMENT) {
|
|
throw "<" + name + "> must be a document collection";
|
|
}
|
|
|
|
if (col === null) {
|
|
throw "collection '" + name + "' has vanished";
|
|
}
|
|
|
|
return col;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief find or create an edge collection by name
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
var findOrCreateEdgeCollectionByName = function (name) {
|
|
var col = db._collection(name);
|
|
|
|
if (col === null) {
|
|
col = db._createEdgeCollection(name);
|
|
} else if (!(col instanceof ArangoCollection) || col.type() !== ArangoCollection.TYPE_EDGE) {
|
|
throw "<" + name + "> must be an edge collection";
|
|
}
|
|
|
|
if (col === null) {
|
|
throw "collection '" + name + "' has vanished";
|
|
}
|
|
|
|
return col;
|
|
};
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief was docuBlock edgeSetProperty
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Edge.prototype.setProperty = function (name, value) {
|
|
var shallow = shallowCopy(this._properties);
|
|
var id;
|
|
|
|
// Could potentially change the weight of edges
|
|
this._graph.emptyCachedPredecessors();
|
|
|
|
shallow.$label = this._properties.$label;
|
|
shallow[name] = value;
|
|
|
|
if (this._properties.hasOwnProperty('_from')) {
|
|
shallow._from = this._properties._from;
|
|
}
|
|
if (this._properties.hasOwnProperty('_to')) {
|
|
shallow._to = this._properties._to;
|
|
}
|
|
id = this._graph._edges.replace(this._properties, shallow);
|
|
this._properties = this._graph._edges.document(id);
|
|
|
|
return value;
|
|
};
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @start Docu Block vertexEdges
|
|
///
|
|
/// `vertex.edges()`
|
|
///
|
|
/// Returns a list of in- or outbound edges of the *vertex*.
|
|
///
|
|
/// @end Docu Block
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Vertex.prototype.edges = function () {
|
|
var graph = this._graph;
|
|
|
|
return graph._edges.edges(this._properties._id).map(function (result) {
|
|
return graph.constructEdge(result);
|
|
});
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @start Docu Block vertexGetInEdges
|
|
///
|
|
///`vertex.getInEdges(label, ...)`
|
|
///
|
|
/// Returns a list of inbound edges of the *vertex* with given label(s).
|
|
///
|
|
/// @end Docu Block
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Vertex.prototype.getInEdges = function () {
|
|
var labels = Array.prototype.slice.call(arguments);
|
|
var result = this.inbound();
|
|
|
|
if (labels.length > 0) {
|
|
result = result.filter(function (edge) {
|
|
return (labels.lastIndexOf(edge.getLabel()) > -1);
|
|
});
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @start Docu Block vertexGetOutEdges
|
|
///
|
|
/// `vertex.getOutEdges(label, ...)`
|
|
///
|
|
/// Returns a list of outbound edges of the *vertex* with given label(s).
|
|
///
|
|
/// @end Docu Block
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Vertex.prototype.getOutEdges = function () {
|
|
var labels = Array.prototype.slice.call(arguments);
|
|
var result = this.outbound();
|
|
|
|
if (labels.length > 0) {
|
|
result = result.filter(function (edge) {
|
|
return (labels.lastIndexOf(edge.getLabel()) > -1);
|
|
});
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @start Docu Block vertexGetEdges
|
|
///
|
|
/// `vertex.getEdges(label, ...)`
|
|
///
|
|
/// Returns a list of in- or outbound edges of the *vertex* with given
|
|
/// label(s).
|
|
/// @end Docu Block
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Vertex.prototype.getEdges = function () {
|
|
var labels = Array.prototype.slice.call(arguments);
|
|
var result = this.edges();
|
|
|
|
if (labels.length > 0) {
|
|
result = result.filter(function (edge) {
|
|
return (labels.lastIndexOf(edge.getLabel()) > -1);
|
|
});
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @start Docu Block vertexInbound
|
|
///
|
|
/// `vertex.inbound()`
|
|
///
|
|
/// Returns a list of inbound edges of the *vertex*.
|
|
/// @end Docu Block
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Vertex.prototype.inbound = function () {
|
|
var graph = this._graph;
|
|
|
|
return graph._edges.inEdges(this._properties._id).map(function (result) {
|
|
return graph.constructEdge(result);
|
|
});
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @start Docu Block vertexOutbound
|
|
///
|
|
/// `vertex.outbound()`
|
|
///
|
|
/// Returns a list of outbound edges of the *vertex*.
|
|
///
|
|
/// @end Docu Block
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Vertex.prototype.outbound = function () {
|
|
var graph = this._graph;
|
|
|
|
return graph._edges.outEdges(this._properties._id).map(function (result) {
|
|
return graph.constructEdge(result);
|
|
});
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @start Docu Block vertexSetProperty
|
|
///
|
|
/// `vertex.setProperty(name, value)`
|
|
///
|
|
/// Changes or sets the property *name* a *vertex* to *value*.
|
|
///
|
|
/// @end Docu BLock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Vertex.prototype.setProperty = function (name, value) {
|
|
var shallow = shallowCopy(this._properties);
|
|
var id;
|
|
|
|
shallow[name] = value;
|
|
|
|
// TODO use "update" if this becomes available
|
|
id = this._graph._vertices.replace(this._properties, shallow);
|
|
this._properties = this._graph._vertices.document(id);
|
|
|
|
return value;
|
|
};
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @start Docu Block graphConstruct
|
|
///
|
|
/// `Graph(name, vertices, edges)`
|
|
///
|
|
/// Constructs a new graph object using the collection *vertices* for all
|
|
/// vertices and the collection *edges* for all edges. Note that it is
|
|
/// possible to construct two graphs with the same vertex set, but different
|
|
/// edge sets.
|
|
///
|
|
/// `Graph(name)`
|
|
///
|
|
/// Returns a known graph.
|
|
///
|
|
/// @end Docu Block
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Graph.prototype.initialize = function (name, vertices, edges, waitForSync) {
|
|
this._name = name;
|
|
var gdb = db._collection("_graphs");
|
|
var graphProperties;
|
|
var graphPropertiesId;
|
|
|
|
if (gdb === null) {
|
|
throw "_graphs collection does not exist.";
|
|
}
|
|
|
|
if (typeof name !== "string" || name === "") {
|
|
throw "<name> must be a string";
|
|
}
|
|
|
|
// convert collection objects to collection names
|
|
if (typeof vertices === 'object' && typeof vertices.name === 'function') {
|
|
vertices = vertices.name();
|
|
}
|
|
|
|
if (typeof edges === 'object' && typeof edges.name === 'function') {
|
|
edges = edges.name();
|
|
}
|
|
|
|
// find an existing graph by name
|
|
if (vertices === undefined && edges === undefined) {
|
|
|
|
try {
|
|
graphProperties = gdb.document(name);
|
|
}
|
|
catch (e) {
|
|
throw "no graph named '" + name + "' found";
|
|
}
|
|
|
|
if (graphProperties === null) {
|
|
throw "no graph named '" + name + "' found";
|
|
}
|
|
|
|
//check if graph can be loaded by this deprecated module
|
|
var newGraphError = "Graph can not be loaded, "
|
|
+ "because more than 1 vertex collection is defined. "
|
|
+ "Please use the new graph module";
|
|
var edgeDefinitions = db._graphs.document(name).edgeDefinitions;
|
|
if (edgeDefinitions.length === 0) {
|
|
throw newGraphError;
|
|
}
|
|
if (edgeDefinitions.length > 1) {
|
|
throw newGraphError;
|
|
} else if (edgeDefinitions.length === 1) {
|
|
var from = edgeDefinitions[0].from;
|
|
var to = edgeDefinitions[0].to;
|
|
if (from.length !== 1 || to.length !== 1 || from[0] !== to[0]) {
|
|
throw newGraphError;
|
|
}
|
|
}
|
|
|
|
vertices = db._collection(edgeDefinitions[0].from[0]);
|
|
|
|
if (vertices === null) {
|
|
throw "vertex collection '" + edgeDefinitions[0].from[0] + "' has vanished";
|
|
}
|
|
|
|
edges = db._collection(edgeDefinitions[0].collection);
|
|
|
|
if (edges === null) {
|
|
throw "edge collection '" + edgeDefinitions[0].collection + "' has vanished";
|
|
}
|
|
}
|
|
|
|
// sanity check for vertices
|
|
else if (typeof vertices !== "string" || vertices === "") {
|
|
throw "<vertices> must be a string or null";
|
|
}
|
|
|
|
// sanity check for edges
|
|
else if (typeof edges !== "string" || edges === "") {
|
|
throw "<edges> must be a string or null";
|
|
}
|
|
|
|
// create a new graph or get an existing graph
|
|
else {
|
|
try {
|
|
graphProperties = gdb.document(name);
|
|
}
|
|
catch (e1) {
|
|
graphProperties = null;
|
|
}
|
|
|
|
// graph doesn't exist yet, create it
|
|
if (graphProperties === null) {
|
|
|
|
// check if know that graph
|
|
graphProperties = gdb.firstExample(
|
|
'edgeDefintions', [{"collection": edges, "from" :[vertices], "to": [vertices]}]
|
|
);
|
|
|
|
if (graphProperties === null) {
|
|
|
|
// check if edge is used in a graph
|
|
gdb.toArray().forEach(
|
|
function(singleGraph) {
|
|
var sGEDs = singleGraph.edgeDefinitions;
|
|
sGEDs.forEach(
|
|
function(sGED) {
|
|
if (sGED.collection === edges) {
|
|
graphProperties = "";
|
|
}
|
|
}
|
|
);
|
|
}
|
|
);
|
|
|
|
if (graphProperties === null) {
|
|
findOrCreateCollectionByName(vertices);
|
|
findOrCreateEdgeCollectionByName(edges);
|
|
|
|
var newEdgeDefinition = [{"collection": edges, "from" :[vertices], "to": [vertices]}];
|
|
|
|
graphPropertiesId = gdb.save(
|
|
{
|
|
'edgeDefinitions' : newEdgeDefinition,
|
|
'_key' : name
|
|
},
|
|
waitForSync
|
|
);
|
|
|
|
graphProperties = gdb.document(graphPropertiesId._key);
|
|
}
|
|
else {
|
|
throw "edge collection already used";
|
|
}
|
|
}
|
|
else {
|
|
throw "found graph but has different <name>";
|
|
}
|
|
}
|
|
else {
|
|
if (graphProperties.edgeDefinitions[0].from[0] !== vertices
|
|
|| graphProperties.edgeDefinitions[0].to[0] !== vertices
|
|
|| graphProperties.edgeDefinitions[0].collection !== edges) {
|
|
throw "graph with that name already exists!";
|
|
}
|
|
}
|
|
|
|
vertices = db._collection(graphProperties.edgeDefinitions[0].from[0]);
|
|
edges = db._collection(graphProperties.edgeDefinitions[0].collection);
|
|
}
|
|
this._properties = graphProperties;
|
|
|
|
// and store the collections
|
|
this._gdb = gdb;
|
|
this._vertices = vertices;
|
|
this._edges = edges;
|
|
|
|
// and dictionary for vertices and edges
|
|
this._verticesCache = {};
|
|
this._edgesCache = {};
|
|
|
|
// and store the caches
|
|
this.predecessors = {};
|
|
this.distances = {};
|
|
};
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @start Docu Block JSF_graph_getAll
|
|
///
|
|
/// `graph.getAll()`
|
|
///
|
|
/// Returns all available graphs.
|
|
/// @end Docu Block
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Graph.getAll = function getAllGraphs () {
|
|
var gdb = db._collection("_graphs"),
|
|
graphs = [ ];
|
|
|
|
gdb.toArray().forEach(function(doc) {
|
|
try {
|
|
var g = new Graph(doc._key);
|
|
|
|
if (g._properties !== null) {
|
|
graphs.push(g._properties);
|
|
}
|
|
}
|
|
catch (err) {
|
|
// if there's a problem, we just skip this graph
|
|
}
|
|
});
|
|
|
|
return graphs;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief static drop function
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Graph.drop = function (name, waitForSync) {
|
|
var gdb = db._collection("_graphs");
|
|
var exists = gdb.exists(name);
|
|
|
|
try {
|
|
var obj = new Graph(name);
|
|
return obj.drop(waitForSync);
|
|
}
|
|
catch (err) {
|
|
if (exists) {
|
|
// if the graph exists but cannot be deleted because one of the underlying
|
|
// collections is missing, delete from _graphs "manually"
|
|
gdb.remove(name, true, waitForSync);
|
|
}
|
|
}
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @start Docu Block graphDrop
|
|
///
|
|
/// `graph.drop(waitForSync)`
|
|
///
|
|
/// Drops the graph, the vertices, and the edges. Handle with care.
|
|
/// @end Docu Block
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Graph.prototype.drop = function (waitForSync) {
|
|
newGraph._drop(this._name, true);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief saves an edge to the graph
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Graph.prototype._saveEdge = function(id, out_vertex_id, in_vertex_id, shallow, waitForSync) {
|
|
this.emptyCachedPredecessors();
|
|
|
|
if (id !== undefined && id !== null) {
|
|
shallow._key = String(id);
|
|
}
|
|
|
|
var ref = this._edges.save(out_vertex_id,
|
|
in_vertex_id,
|
|
shallow,
|
|
waitForSync);
|
|
|
|
return this.constructEdge(ref._id);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief saves a vertex to the graph
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Graph.prototype._saveVertex = function (id, shallow, waitForSync) {
|
|
var ref;
|
|
|
|
if (is.existy(id)) {
|
|
shallow._key = String(id);
|
|
}
|
|
|
|
ref = this._vertices.save(shallow, waitForSync);
|
|
|
|
return this.constructVertex(ref._id);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief replaces a vertex to the graph
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Graph.prototype._replaceVertex = function (vertex_id, data) {
|
|
this._vertices.replace(vertex_id, data);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief replaces an edge in the graph
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Graph.prototype._replaceEdge = function (edge_id, data) {
|
|
this._edges.replace(edge_id, data);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @start Docu Block graphGetVertex
|
|
///
|
|
/// `graph.getVertex(id)`
|
|
///
|
|
/// Returns the vertex identified by *id* or *null*.
|
|
///
|
|
/// @end Docu Block
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Graph.prototype.getVertex = function (id) {
|
|
try {
|
|
return this.constructVertex(id);
|
|
}
|
|
catch (e) {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @start Docu Block GraphGetVertices
|
|
///
|
|
/// `graph.getVertices()`
|
|
///
|
|
/// Returns an iterator for all vertices of the graph. The iterator supports the
|
|
/// methods *hasNext* and *next*.
|
|
///
|
|
/// @end Docu Block
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Graph.prototype.getVertices = function () {
|
|
var all = this._vertices.all(),
|
|
graph = this,
|
|
wrapper = function(object) {
|
|
return graph.constructVertex(object);
|
|
};
|
|
|
|
return new Iterator(wrapper, all, "[edge iterator]");
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @start Docu Block graphGetEdge
|
|
///
|
|
/// `graph.getEdge(id)`
|
|
///
|
|
/// Returns the edge identified by *id* or *null*.
|
|
///
|
|
/// @end Docu Block
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Graph.prototype.getEdge = function (id) {
|
|
var ref, edge;
|
|
|
|
try {
|
|
ref = this._edges.document(id);
|
|
} catch (e) {
|
|
ref = null;
|
|
}
|
|
|
|
if (ref !== null) {
|
|
edge = this.constructEdge(ref);
|
|
} else {
|
|
try {
|
|
edge = this.constructEdge(id);
|
|
} catch (e1) {
|
|
edge = null;
|
|
}
|
|
}
|
|
|
|
return edge;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @start Docu Block graphGetEdges
|
|
///
|
|
/// `graph.getEdges()`
|
|
///
|
|
/// Returns an iterator for all edges of the graph. The iterator supports the
|
|
/// methods *hasNext* and *next*.
|
|
///
|
|
/// @end Docu Block
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Graph.prototype.getEdges = function () {
|
|
var all = this._edges.all(),
|
|
graph = this,
|
|
wrapper = function(object) {
|
|
return graph.constructEdge(object);
|
|
};
|
|
return new Iterator(wrapper, all, "[edge iterator]");
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @start Docu Block graphRemoveVertex
|
|
///
|
|
/// `graph.removeVertex(vertex, waitForSync)`
|
|
///
|
|
/// Deletes the *vertex* and all its edges.
|
|
///
|
|
/// @end Docu Block
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Graph.prototype.removeVertex = function (vertex, waitForSync) {
|
|
var result, graph = this;
|
|
|
|
this.emptyCachedPredecessors();
|
|
|
|
if (vertex._properties) {
|
|
result = this._vertices.remove(vertex._properties, true, waitForSync);
|
|
|
|
if (!result) {
|
|
throw "cannot delete vertex";
|
|
}
|
|
|
|
vertex.edges().forEach(function (edge) {
|
|
graph.removeEdge(edge, waitForSync);
|
|
});
|
|
|
|
vertex._properties = undefined;
|
|
}
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @start Docu Block graphRemoveEdge
|
|
///
|
|
/// `graph.removeEdge(vertex, waitForSync)`
|
|
///
|
|
/// Deletes the *edge*. Note that the in and out vertices are left untouched.
|
|
///
|
|
/// @end Docu Block
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Graph.prototype.removeEdge = function (edge, waitForSync) {
|
|
var result;
|
|
|
|
this.emptyCachedPredecessors();
|
|
|
|
if (edge._properties) {
|
|
result = this._edges.remove(edge._properties, true, waitForSync);
|
|
|
|
if (! result) {
|
|
throw "cannot delete edge";
|
|
}
|
|
|
|
this._edgesCache[edge._properties._id] = undefined;
|
|
edge._properties = undefined;
|
|
}
|
|
};
|
|
|
|
|
|
exports.Edge = Edge;
|
|
exports.Graph = Graph;
|
|
exports.Vertex = Vertex;
|
|
exports.GraphArray = GraphArray;
|
|
|
|
require("@arangodb/graph/algorithms-common");
|
|
|
|
|
|
|