1
0
Fork 0
arangodb/js/common/modules/@arangodb/general-graph.js

2491 lines
80 KiB
JavaScript

/*jshint strict: false */
/*global ArangoClusterComm */
////////////////////////////////////////////////////////////////////////////////
/// @brief Graph functionality
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2014 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 Florian Bartels, Michael Hackstein, Guido Schwab
/// @author Copyright 2011-2014, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
var arangodb = require("@arangodb"),
internal = require("internal"),
ArangoCollection = arangodb.ArangoCollection,
ArangoError = arangodb.ArangoError,
db = arangodb.db,
errors = arangodb.errors,
_ = require("lodash");
////////////////////////////////////////////////////////////////////////////////
/// @brief transform a string into an array.
////////////////////////////////////////////////////////////////////////////////
var stringToArray = function (x) {
if (typeof x === "string") {
return [x];
}
return x.slice();
};
////////////////////////////////////////////////////////////////////////////////
/// @brief checks if a parameter is not defined, an empty string or an empty
// array
////////////////////////////////////////////////////////////////////////////////
var isValidCollectionsParameter = function (x) {
if (!x) {
return false;
}
if (Array.isArray(x) && x.length === 0) {
return false;
}
if (typeof x !== "string" && !Array.isArray(x)) {
return false;
}
return true;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief find or create a collection by name
////////////////////////////////////////////////////////////////////////////////
var findOrCreateCollectionByName = function (name, type, noCreate) {
var col = db._collection(name),
res = false;
if (col === null && ! noCreate) {
if (type === ArangoCollection.TYPE_DOCUMENT) {
col = db._create(name);
} else {
col = db._createEdgeCollection(name);
}
res = true;
}
else if (! (col instanceof ArangoCollection)) {
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_NOT_AN_ARANGO_COLLECTION.code;
err.errorMessage = name + arangodb.errors.ERROR_GRAPH_NOT_AN_ARANGO_COLLECTION.message;
throw err;
} else if (type === ArangoCollection.TYPE_EDGE && col.type() !== type) {
var err2 = new ArangoError();
err2.errorNum = arangodb.errors.ERROR_ARANGO_COLLECTION_TYPE_INVALID.code;
err2.errorMessage = name + " cannot be used as relation. It is not an edge collection";
throw err2;
}
return res;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief find or create a collection by name
////////////////////////////////////////////////////////////////////////////////
var findOrCreateCollectionsByEdgeDefinitions = function (edgeDefinitions, noCreate) {
var vertexCollections = {},
edgeCollections = {};
edgeDefinitions.forEach(function (e) {
if (! e.hasOwnProperty('collection') ||
! e.hasOwnProperty('from') ||
! e.hasOwnProperty('to') ||
! Array.isArray(e.from) ||
! Array.isArray(e.to)) {
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_CREATE_MALFORMED_EDGE_DEFINITION.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_CREATE_MALFORMED_EDGE_DEFINITION.message;
throw err;
}
e.from.concat(e.to).forEach(function (v) {
findOrCreateCollectionByName(v, ArangoCollection.TYPE_DOCUMENT, noCreate);
vertexCollections[v] = db[v];
});
findOrCreateCollectionByName(e.collection, ArangoCollection.TYPE_EDGE, noCreate);
edgeCollections[e.collection] = db[e.collection];
});
return [
vertexCollections,
edgeCollections
];
};
////////////////////////////////////////////////////////////////////////////////
/// @brief internal function to get graphs collection
////////////////////////////////////////////////////////////////////////////////
var getGraphCollection = function() {
var gCol = db._graphs;
if (gCol === null || gCol === undefined) {
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_NO_GRAPH_COLLECTION.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_NO_GRAPH_COLLECTION.message;
throw err;
}
return gCol;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief internal function to print edge definitions in _PRINT
////////////////////////////////////////////////////////////////////////////////
var printEdgeDefinitions = function(defs) {
return _.map(defs, function(d) {
var out = d.collection;
out += ": [";
out += d.from.join(", ");
out += "] -> [";
out += d.to.join(", ");
out += "]";
return out;
});
};
////////////////////////////////////////////////////////////////////////////////
/// @brief internal function to wrap arango collections for overwrite
////////////////////////////////////////////////////////////////////////////////
var wrapCollection = function(col) {
var wrapper = {};
_.each(_.functions(col), function(func) {
wrapper[func] = function() {
return col[func].apply(col, arguments);
};
});
return wrapper;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_example_description
////////////////////////////////////////////////////////////////////////////////
var transformExample = function(example) {
if (example === undefined) {
return {};
}
if (typeof example === "string") {
return {_id: example};
}
if (typeof example === "object") {
if (Array.isArray(example)) {
return _.map(example, function(e) {
if (typeof e === "string") {
return {_id: e};
}
return e;
});
}
return example;
}
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_INVALID_EXAMPLE_ARRAY_OBJECT_STRING.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_INVALID_EXAMPLE_ARRAY_OBJECT_STRING.message;
throw err;
};
var checkAllowsRestriction = function(list, rest, msg) {
var unknown = [];
var colList = _.map(list, function(item) {
return item.name();
});
_.each(rest, function(r) {
if (!_.contains(colList, r)) {
unknown.push(r);
}
});
if (unknown.length > 0) {
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_BAD_PARAMETER.code;
err.errorMessage = msg + ": "
+ unknown.join(" and ")
+ " are not known to the graph";
throw err;
}
return true;
};
var AQLStatement = function(query, type) {
this.query = query;
if (type) {
this.type = type;
}
};
AQLStatement.prototype.printQuery = function() {
return this.query;
};
AQLStatement.prototype.isPathQuery = function() {
return this.type === "path";
};
AQLStatement.prototype.isPathVerticesQuery = function() {
return this.type === "pathVertices";
};
AQLStatement.prototype.isPathEdgesQuery = function() {
return this.type === "pathEdges";
};
AQLStatement.prototype.isEdgeQuery = function() {
return this.type === "edge";
};
AQLStatement.prototype.isVertexQuery = function() {
return this.type === "vertex";
};
AQLStatement.prototype.isNeighborQuery = function() {
return this.type === "neighbor";
};
AQLStatement.prototype.allowsRestrict = function() {
return this.isEdgeQuery()
|| this.isVertexQuery()
|| this.isNeighborQuery();
};
////////////////////////////////////////////////////////////////////////////////
/// @brief Starting point of the fluent interface.
///
/// Only for internal use.
////////////////////////////////////////////////////////////////////////////////
var AQLGenerator = function(graph) {
this.stack = [];
this.callStack = [];
this.bindVars = {
"graphName": graph.__name
};
this.graph = graph;
this.cursor = null;
this.lastVar = "";
this._path = [];
this._pathVertices = [];
this._pathEdges = [];
this._getPath = false;
};
AQLGenerator.prototype._addToPrint = function(name) {
var args = Array.prototype.slice.call(arguments);
args.shift(); // The Name
var stackEntry = {};
stackEntry.name = name;
if (args.length > 0 && args[0] !== undefined) {
stackEntry.params = args;
} else {
stackEntry.params = [];
}
this.callStack.push(stackEntry);
};
////////////////////////////////////////////////////////////////////////////////
/// @brief Print the call stack of this query
////////////////////////////////////////////////////////////////////////////////
AQLGenerator.prototype._PRINT = function(context) {
context.output = "[ GraphAQL ";
context.output += this.graph.__name;
_.each(this.callStack, function(call) {
if(context.prettyPrint) {
context.output += "\n";
}
context.output += ".";
context.output += call.name;
context.output += "(";
var i = 0;
for(i = 0; i < call.params.length; ++i) {
if (i > 0) {
context.output += ", ";
}
internal.printRecursive(call.params[i], context);
}
context.output += ")";
});
context.output += " ] ";
};
////////////////////////////////////////////////////////////////////////////////
/// @brief Dispose and reset the current cursor of the query
///
/// Only for internal use.
////////////////////////////////////////////////////////////////////////////////
AQLGenerator.prototype._clearCursor = function() {
if (this.cursor) {
this.cursor.dispose();
this.cursor = null;
}
};
////////////////////////////////////////////////////////////////////////////////
/// @brief Execute the query and keep the cursor
///
/// Only for internal use.
////////////////////////////////////////////////////////////////////////////////
AQLGenerator.prototype._createCursor = function() {
if (!this.cursor) {
this.cursor = this.execute();
}
};
////////////////////////////////////////////////////////////////////////////////
/// @brief General edge query, takes direction as parameter
///
/// This will create the general AQL statement to load edges
/// connected to the vertices selected in the step before.
/// Will also bind the options into bindVars.
///
/// Only for internal use, user gets different functions for directions
////////////////////////////////////////////////////////////////////////////////
AQLGenerator.prototype._edges = function(edgeExample, options) {
this._clearCursor();
this.options = options || {};
var ex = transformExample(edgeExample);
var edgeName = "edges_" + this.stack.length;
var query = "FOR " + edgeName
+ ' IN GRAPH_EDGES(@graphName';
if (!this.getLastVar()) {
query += ',{}';
} else {
query += ',' + this.getLastVar();
}
query += ',@options_'
+ this.stack.length + ')';
if (!Array.isArray(ex)) {
ex = [ex];
}
this.options.edgeExamples = ex;
this.options.includeData = true;
this.bindVars["options_" + this.stack.length] = this.options;
var stmt = new AQLStatement(query, "edge");
this.stack.push(stmt);
this.lastVar = edgeName;
this._path.push(edgeName);
this._pathEdges.push(edgeName);
return this;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_fluent_aql_edges
////////////////////////////////////////////////////////////////////////////////
AQLGenerator.prototype.edges = function(example) {
this._addToPrint("edges", example);
return this._edges(example, {direction: "any"});
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_fluent_aql_outEdges
////////////////////////////////////////////////////////////////////////////////
AQLGenerator.prototype.outEdges = function(example) {
this._addToPrint("outEdges", example);
return this._edges(example, {direction: "outbound"});
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_fluent_aql_inEdges
////////////////////////////////////////////////////////////////////////////////
AQLGenerator.prototype.inEdges = function(example) {
this._addToPrint("inEdges", example);
return this._edges(example, {direction: "inbound"});
};
////////////////////////////////////////////////////////////////////////////////
/// @brief General vertex query, takes direction as parameter
///
/// This will create the general AQL statement to load vertices
/// connected to the edges selected in the step before.
/// Will also bind the options into bindVars.
///
/// Only for internal use, user gets different functions for directions
////////////////////////////////////////////////////////////////////////////////
AQLGenerator.prototype._vertices = function(example, options, mergeWith) {
this._clearCursor();
this.options = options || {};
var ex = transformExample(example);
var vertexName = "vertices_" + this.stack.length;
var query = "FOR " + vertexName
+ " IN GRAPH_VERTICES(@graphName,";
if (mergeWith !== undefined) {
if (Array.isArray(mergeWith)) {
var i;
query += "[";
for (i = 0; i < mergeWith.length; ++i) {
if (i > 0) {
query += ",";
}
query += "MERGE(@vertexExample_" + this.stack.length
+ "," + mergeWith[i] + ")";
}
query += "]";
} else {
if (Array.isArray(ex)) {
query += "@vertexExample_" + this.stack.length + " [ * RETURN MERGE(CURRENT," + mergeWith + ")]";
}
else {
query += "MERGE(@vertexExample_" + this.stack.length
+ "," + mergeWith + ")";
}
}
} else {
query += "@vertexExample_" + this.stack.length;
}
query += ',@options_' + this.stack.length + ')';
this.bindVars["vertexExample_" + this.stack.length] = ex;
this.bindVars["options_" + this.stack.length] = this.options;
var stmt = new AQLStatement(query, "vertex");
this.stack.push(stmt);
this.lastVar = vertexName;
this._path.push(vertexName);
this._pathVertices.push(vertexName);
return this;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_fluent_aql_vertices
////////////////////////////////////////////////////////////////////////////////
AQLGenerator.prototype.vertices = function(example) {
this._addToPrint("vertices", example);
if (!this.getLastVar()) {
return this._vertices(example);
}
var edgeVar = this.getLastVar();
return this._vertices(example, undefined,
["{'_id': " + edgeVar + "._from}", "{'_id': " + edgeVar + "._to}"]);
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_fluent_aql_fromVertices
////////////////////////////////////////////////////////////////////////////////
AQLGenerator.prototype.fromVertices = function(example) {
this._addToPrint("fromVertices", example);
if (!this.getLastVar()) {
return this._vertices(example);
}
var edgeVar = this.getLastVar();
return this._vertices(example, undefined, "{'_id': " + edgeVar + "._from}");
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_fluent_aql_toVertices
////////////////////////////////////////////////////////////////////////////////
AQLGenerator.prototype.toVertices = function(example) {
this._addToPrint("toVertices", example);
if (!this.getLastVar()) {
return this._vertices(example);
}
var edgeVar = this.getLastVar();
return this._vertices(example, undefined, "{'_id': " + edgeVar + "._to}");
};
////////////////////////////////////////////////////////////////////////////////
/// @brief Get the variable holding the last result
///
/// Only for internal use.
/// The return statement of the AQL query has to return
/// this value.
/// Also chaining has to use this variable to restrict
/// queries in the next step to only values from this set.
////////////////////////////////////////////////////////////////////////////////
AQLGenerator.prototype.getLastVar = function() {
if (this.lastVar === "") {
return false;
}
return this.lastVar;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_fluent_aql_path
////////////////////////////////////////////////////////////////////////////////
AQLGenerator.prototype.path = function() {
this._clearCursor();
var statement = new AQLStatement("", "path");
this.stack.push(statement);
return this;
};
AQLGenerator.prototype.pathVertices = function() {
this._clearCursor();
var statement = new AQLStatement("", "pathVertices");
this.stack.push(statement);
return this;
};
AQLGenerator.prototype.pathEdges = function() {
this._clearCursor();
var statement = new AQLStatement("", "pathEdges");
this.stack.push(statement);
return this;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_fluent_aql_neighbors
////////////////////////////////////////////////////////////////////////////////
AQLGenerator.prototype.neighbors = function(vertexExample, options) {
this._addToPrint("neighbors", vertexExample, options);
var ex = transformExample(vertexExample);
var resultName = "neighbors_" + this.stack.length;
var query = "FOR " + resultName
+ " IN GRAPH_NEIGHBORS(@graphName,"
+ this.getLastVar()
+ ',@options_'
+ this.stack.length + ')';
var opts;
if (options) {
opts = _.extend({}, options);
} else {
opts = {};
}
opts.neighborExamples = ex;
opts.includeData = true;
this.bindVars["options_" + this.stack.length] = opts;
var stmt = new AQLStatement(query, "neighbor");
this.stack.push(stmt);
this.lastVar = resultName;
this._path.push(resultName);
this._pathVertices.push(resultName);
/*
this.lastVar = resultName + ".vertex";
this._path.push(resultName + ".path");
this._pathVertices.push("SLICE(" + resultName + ".path.vertices, 1)");
this._pathEdges.push(resultName + ".path.edges");
*/
return this;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief Get the last statement that can be restricted to collections
///
/// Only for internal use.
/// This returnes the last statement that can be restricted to
/// specific collections.
/// Required to allow a chaining of `restrict` after `filter` for instance.
////////////////////////////////////////////////////////////////////////////////
AQLGenerator.prototype._getLastRestrictableStatementInfo = function() {
var i = this.stack.length - 1;
while (!this.stack[i].allowsRestrict()) {
i--;
}
return {
statement: this.stack[i],
options: this.bindVars["options_" + i]
};
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_fluent_aql_restrict
////////////////////////////////////////////////////////////////////////////////
AQLGenerator.prototype.restrict = function(restrictions) {
var rest = stringToArray(restrictions);
if (rest.length === 0) {
return this;
}
this._addToPrint("restrict", restrictions);
this._clearCursor();
var lastQueryInfo = this._getLastRestrictableStatementInfo();
var lastQuery = lastQueryInfo.statement;
var opts = lastQueryInfo.options;
var restricts;
if (lastQuery.isEdgeQuery()) {
checkAllowsRestriction(
this.graph._edgeCollections(),
rest,
"edge collections"
);
restricts = opts.edgeCollectionRestriction || [];
opts.edgeCollectionRestriction = restricts.concat(restrictions);
} else if (lastQuery.isVertexQuery() || lastQuery.isNeighborQuery()) {
checkAllowsRestriction(
this.graph._vertexCollections(),
rest,
"vertex collections"
);
restricts = opts.vertexCollectionRestriction || [];
opts.vertexCollectionRestriction = restricts.concat(restrictions);
}
return this;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_fluent_aql_filter
////////////////////////////////////////////////////////////////////////////////
AQLGenerator.prototype.filter = function(example) {
this._addToPrint("filter", example);
this._clearCursor();
var ex = [];
if (Object.prototype.toString.call(example) !== "[object Array]") {
if (Object.prototype.toString.call(example) !== "[object Object]") {
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_INVALID_EXAMPLE_ARRAY_OBJECT.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_INVALID_EXAMPLE_ARRAY_OBJECT.message;
throw err;
}
ex = [example];
} else {
ex = example;
}
var query = "FILTER MATCHES(" + this.getLastVar() + "," + JSON.stringify(ex) + ")";
this.stack.push(new AQLStatement(query));
return this;
};
AQLGenerator.prototype.printQuery = function() {
return this.stack.map(function(stmt) {
return stmt.printQuery();
}).join(" ");
};
AQLGenerator.prototype.execute = function() {
this._clearCursor();
var query = this.printQuery();
var bindVars = this.bindVars;
if (this.stack[this.stack.length-1].isPathQuery()) {
query += " RETURN [" + this._path + "]";
} else if (this.stack[this.stack.length-1].isPathVerticesQuery()) {
query += " RETURN FLATTEN([" + this._pathVertices + "])";
} else if (this.stack[this.stack.length-1].isPathEdgesQuery()) {
query += " RETURN FLATTEN([" + this._pathEdges + "])";
} else {
query += " RETURN " + this.getLastVar();
}
return db._query(query, bindVars, {count: true});
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_fluent_aql_toArray
////////////////////////////////////////////////////////////////////////////////
AQLGenerator.prototype.toArray = function() {
this._createCursor();
return this.cursor.toArray();
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_fluent_aql_count
////////////////////////////////////////////////////////////////////////////////
AQLGenerator.prototype.count = function() {
this._createCursor();
return this.cursor.count();
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_fluent_aql_hasNext
////////////////////////////////////////////////////////////////////////////////
AQLGenerator.prototype.hasNext = function() {
this._createCursor();
return this.cursor.hasNext();
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_fluent_aql_next
////////////////////////////////////////////////////////////////////////////////
AQLGenerator.prototype.next = function() {
this._createCursor();
return this.cursor.next();
};
////////////////////////////////////////////////////////////////////////////////
/// Deprecated block
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_undirectedRelation
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// Deprecated block
////////////////////////////////////////////////////////////////////////////////
var _undirectedRelation = function (relationName, vertexCollections) {
var err;
if (arguments.length < 2) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_INVALID_NUMBER_OF_ARGUMENTS.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_INVALID_NUMBER_OF_ARGUMENTS.message + "2";
throw err;
}
if (typeof relationName !== "string" || relationName === "") {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_INVALID_PARAMETER.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_INVALID_PARAMETER.message + " arg1 must not be empty";
throw err;
}
if (!isValidCollectionsParameter(vertexCollections)) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_INVALID_PARAMETER.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_INVALID_PARAMETER.message + " arg2 must not be empty";
throw err;
}
return {
collection: relationName,
from: stringToArray(vertexCollections),
to: stringToArray(vertexCollections)
};
};
////////////////////////////////////////////////////////////////////////////////
/// Deprecated block
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_directedRelation
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_relation
////////////////////////////////////////////////////////////////////////////////
var _relation = function (
relationName, fromVertexCollections, toVertexCollections) {
var err;
if (arguments.length < 3) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_INVALID_NUMBER_OF_ARGUMENTS.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_INVALID_NUMBER_OF_ARGUMENTS.message + "3";
throw err;
}
if (typeof relationName !== "string" || relationName === "") {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_INVALID_PARAMETER.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_INVALID_PARAMETER.message + " arg1 must be non empty string";
throw err;
}
if (!isValidCollectionsParameter(fromVertexCollections)) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_INVALID_PARAMETER.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_INVALID_PARAMETER.message
+ " arg2 must be non empty string or array";
throw err;
}
if (!isValidCollectionsParameter(toVertexCollections)) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_INVALID_PARAMETER.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_INVALID_PARAMETER.message
+ " arg3 must be non empty string or array";
throw err;
}
return {
collection: relationName,
from: stringToArray(fromVertexCollections),
to: stringToArray(toVertexCollections)
};
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_list
////////////////////////////////////////////////////////////////////////////////
var _list = function() {
var gdb = getGraphCollection();
return _.pluck(gdb.toArray(), "_key");
};
var _listObjects = function() {
return getGraphCollection().toArray();
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_edge_definitions
////////////////////////////////////////////////////////////////////////////////
var _edgeDefinitions = function () {
var res = [], args = arguments;
Object.keys(args).forEach(function (x) {
res.push(args[x]);
});
return res;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_extend_edge_definitions
////////////////////////////////////////////////////////////////////////////////
var _extendEdgeDefinitions = function (edgeDefinition) {
var args = arguments, i = 0;
Object.keys(args).forEach(
function (x) {
i++;
if (i === 1) {
return;
}
edgeDefinition.push(args[x]);
}
);
};
////////////////////////////////////////////////////////////////////////////////
/// internal helper to sort a graph's edge definitions
////////////////////////////////////////////////////////////////////////////////
var sortEdgeDefinition = function(edgeDefinition) {
edgeDefinition.from = edgeDefinition.from.sort();
edgeDefinition.to = edgeDefinition.to.sort();
return edgeDefinition;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief create a new graph
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_how_to_create
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_create
////////////////////////////////////////////////////////////////////////////////
var _create = function (graphName, edgeDefinitions, orphanCollections, options) {
if (! Array.isArray(orphanCollections) ) {
orphanCollections = [];
}
var gdb = getGraphCollection(),
err,
graphAlreadyExists = true,
collections,
result;
if (!graphName) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_CREATE_MISSING_NAME.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_CREATE_MISSING_NAME.message;
throw err;
}
edgeDefinitions = edgeDefinitions || [];
if (!Array.isArray(edgeDefinitions)) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_CREATE_MALFORMED_EDGE_DEFINITION.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_CREATE_MALFORMED_EDGE_DEFINITION.message;
throw err;
}
//check, if a collection is already used in a different edgeDefinition
var tmpCollections = [];
var tmpEdgeDefinitions = {};
edgeDefinitions.forEach(
function(edgeDefinition) {
var col = edgeDefinition.collection;
if (tmpCollections.indexOf(col) !== -1) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_COLLECTION_MULTI_USE.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_COLLECTION_MULTI_USE.message;
throw err;
}
tmpCollections.push(col);
tmpEdgeDefinitions[col] = edgeDefinition;
}
);
gdb.toArray().forEach(
function(singleGraph) {
var sGEDs = singleGraph.edgeDefinitions;
sGEDs.forEach(
function(sGED) {
var col = sGED.collection;
if (tmpCollections.indexOf(col) !== -1) {
if (JSON.stringify(sGED) !== JSON.stringify(tmpEdgeDefinitions[col])) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_COLLECTION_USE_IN_MULTI_GRAPHS.code;
err.errorMessage = col + " "
+ arangodb.errors.ERROR_GRAPH_COLLECTION_USE_IN_MULTI_GRAPHS.message;
throw err;
}
}
}
);
}
);
try {
gdb.document(graphName);
} catch (e) {
if (e.errorNum !== errors.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code) {
throw e;
}
graphAlreadyExists = false;
}
if (graphAlreadyExists) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_DUPLICATE.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_DUPLICATE.message;
throw err;
}
collections = findOrCreateCollectionsByEdgeDefinitions(edgeDefinitions, false);
orphanCollections.forEach(
function(oC) {
findOrCreateCollectionByName(oC, ArangoCollection.TYPE_DOCUMENT);
}
);
edgeDefinitions.forEach(
function(eD, index) {
var tmp = sortEdgeDefinition(eD);
edgeDefinitions[index] = tmp;
}
);
orphanCollections = orphanCollections.sort();
var data = gdb.save({
'orphanCollections' : orphanCollections,
'edgeDefinitions' : edgeDefinitions,
'_key' : graphName
}, options);
result = new Graph(graphName, edgeDefinitions, collections[0], collections[1],
orphanCollections, data._rev , data._id);
return result;
};
var createHiddenProperty = function(obj, name, value) {
Object.defineProperty(obj, name, {
enumerable: false,
writable: true
});
obj[name] = value;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief helper for updating binded collections
////////////////////////////////////////////////////////////////////////////////
var removeEdge = function (graphs, edgeCollection, edgeId, self) {
self.__idsToRemove[edgeId] = 1;
graphs.forEach(
function(graph) {
var edgeDefinitions = graph.edgeDefinitions;
if (graph.edgeDefinitions) {
edgeDefinitions.forEach(
function(edgeDefinition) {
var from = edgeDefinition.from;
var to = edgeDefinition.to;
var collection = edgeDefinition.collection;
// if collection of edge to be deleted is in from or to
if (from.indexOf(edgeCollection) !== -1 || to.indexOf(edgeCollection) !== -1) {
//search all edges of the graph
var edges = db._collection(collection).edges(edgeId);
edges.forEach(function(edge) {
// if from is
if(! self.__idsToRemove.hasOwnProperty(edge._id)) {
self.__collectionsToLock[collection] = 1;
removeEdge(graphs, collection, edge._id, self);
}
});
}
}
);
}
}
);
};
var bindEdgeCollections = function(self, edgeCollections) {
_.each(edgeCollections, function(key) {
var obj = db._collection(key);
var wrap = wrapCollection(obj);
// save
var old_save = wrap.save;
wrap.save = function(from, to, data) {
if (typeof from === 'object' && to === undefined) {
data = from;
from = data._from;
to = data._to;
} else if (typeof from === 'string' && typeof to === 'string' && typeof data === 'object') {
data._from = from;
data._to = to;
}
if (typeof from !== 'string' ||
from.indexOf('/') === -1 ||
typeof to !== 'string' ||
to.indexOf('/') === -1) {
// invalid from or to value
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.code;
err.errorMessage = arangodb.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.message;
throw err;
}
//check, if edge is allowed
self.__edgeDefinitions.forEach(
function(edgeDefinition) {
if (edgeDefinition.collection === key) {
var fromCollection = from.split("/")[0];
var toCollection = to.split("/")[0];
if (! _.contains(edgeDefinition.from, fromCollection)
|| ! _.contains(edgeDefinition.to, toCollection)) {
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_INVALID_EDGE.code;
err.errorMessage =
arangodb.errors.ERROR_GRAPH_INVALID_EDGE.message + " between " + from + " and " + to + ".";
throw err;
}
}
}
);
return old_save(data);
};
// remove
wrap.remove = function(edgeId, options) {
//if _key make _id (only on 1st call)
if (edgeId.indexOf("/") === -1) {
edgeId = key + "/" + edgeId;
}
var graphs = getGraphCollection().toArray();
var edgeCollection = edgeId.split("/")[0];
self.__collectionsToLock[edgeCollection] = 1;
removeEdge(graphs, edgeCollection, edgeId, self);
try {
db._executeTransaction({
collections: {
write: Object.keys(self.__collectionsToLock)
},
embed: true,
action: function (params) {
var db = require("internal").db;
params.ids.forEach(
function(edgeId) {
if (params.options) {
db._remove(edgeId, params.options);
} else {
db._remove(edgeId);
}
}
);
},
params: {
ids: Object.keys(self.__idsToRemove),
options: options
}
});
} catch (e) {
self.__idsToRemove = {};
self.__collectionsToLock = {};
throw e;
}
self.__idsToRemove = {};
self.__collectionsToLock = {};
return true;
};
self[key] = wrap;
});
};
var bindVertexCollections = function(self, vertexCollections) {
_.each(vertexCollections, function(key) {
var obj = db._collection(key);
var wrap = wrapCollection(obj);
wrap.remove = function(vertexId, options) {
//delete all edges using the vertex in all graphs
var graphs = getGraphCollection().toArray();
var vertexCollectionName = key;
if (vertexId.indexOf("/") === -1) {
vertexId = key + "/" + vertexId;
}
self.__collectionsToLock[vertexCollectionName] = 1;
graphs.forEach(
function(graph) {
var edgeDefinitions = graph.edgeDefinitions;
if (graph.edgeDefinitions) {
edgeDefinitions.forEach(
function(edgeDefinition) {
var from = edgeDefinition.from;
var to = edgeDefinition.to;
var collection = edgeDefinition.collection;
if (from.indexOf(vertexCollectionName) !== -1
|| to.indexOf(vertexCollectionName) !== -1
) {
var edges = db._collection(collection).edges(vertexId);
if (edges.length > 0) {
self.__collectionsToLock[collection] = 1;
edges.forEach(function(edge) {
removeEdge(graphs, collection, edge._id, self);
});
}
}
}
);
}
}
);
try {
db._executeTransaction({
collections: {
write: Object.keys(self.__collectionsToLock)
},
embed: true,
action: function (params) {
var db = require("internal").db;
params.ids.forEach(
function(edgeId) {
if (params.options) {
db._remove(edgeId, params.options);
} else {
db._remove(edgeId);
}
}
);
if (params.options) {
db._remove(params.vertexId, params.options);
} else {
db._remove(params.vertexId);
}
},
params: {
ids: Object.keys(self.__idsToRemove),
options: options,
vertexId: vertexId
}
});
} catch (e) {
self.__idsToRemove = {};
self.__collectionsToLock = {};
throw e;
}
self.__idsToRemove = {};
self.__collectionsToLock = {};
return true;
};
self[key] = wrap;
});
};
var updateBindCollections = function(graph) {
//remove all binded collections
Object.keys(graph).forEach(
function(key) {
if(key.substring(0,1) !== "_") {
delete graph[key];
}
}
);
graph.__edgeDefinitions.forEach(
function(edgeDef) {
bindEdgeCollections(graph, [edgeDef.collection]);
bindVertexCollections(graph, edgeDef.from);
bindVertexCollections(graph, edgeDef.to);
}
);
bindVertexCollections(graph, graph.__orphanCollections);
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_vertex_collection_save
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_vertex_collection_replace
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_vertex_collection_update
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_vertex_collection_remove
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_edge_collection_save
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_edge_collection_replace
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_edge_collection_update
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_edge_collection_remove
////////////////////////////////////////////////////////////////////////////////
var Graph = function(graphName, edgeDefinitions, vertexCollections, edgeCollections,
orphanCollections, revision, id) {
edgeDefinitions.forEach(
function(eD, index) {
var tmp = sortEdgeDefinition(eD);
edgeDefinitions[index] = tmp;
}
);
if (!orphanCollections) {
orphanCollections = [];
}
// we can call the "fast" version of some edge functions if we are
// running server-side and are not a coordinator
var useBuiltIn = (typeof ArangoClusterComm === "object");
if (useBuiltIn && require("@arangodb/cluster").isCoordinator()) {
useBuiltIn = false;
}
var self = this;
// Create Hidden Properties
createHiddenProperty(this, "__useBuiltIn", useBuiltIn);
createHiddenProperty(this, "__name", graphName);
createHiddenProperty(this, "__vertexCollections", vertexCollections);
createHiddenProperty(this, "__edgeCollections", edgeCollections);
createHiddenProperty(this, "__edgeDefinitions", edgeDefinitions);
createHiddenProperty(this, "__idsToRemove", {});
createHiddenProperty(this, "__collectionsToLock", {});
createHiddenProperty(this, "__id", id);
createHiddenProperty(this, "__rev", revision);
createHiddenProperty(this, "__orphanCollections", orphanCollections);
updateBindCollections(self);
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_graph
////////////////////////////////////////////////////////////////////////////////
var _graph = function(graphName) {
var gdb = getGraphCollection(),
g, collections, orphanCollections;
try {
g = gdb.document(graphName);
}
catch (e) {
if (e.errorNum !== errors.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code) {
throw e;
}
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_NOT_FOUND.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_NOT_FOUND.message;
throw err;
}
collections = findOrCreateCollectionsByEdgeDefinitions(g.edgeDefinitions, true);
orphanCollections = g.orphanCollections;
if (!orphanCollections) {
orphanCollections = [];
}
return new Graph(graphName, g.edgeDefinitions, collections[0], collections[1], orphanCollections,
g._rev, g._id);
};
////////////////////////////////////////////////////////////////////////////////
/// @brief check if a graph exists.
////////////////////////////////////////////////////////////////////////////////
var _exists = function(graphId) {
var gCol = getGraphCollection();
return gCol.exists(graphId);
};
////////////////////////////////////////////////////////////////////////////////
/// @brief rename a collection inside the _graphs collections
////////////////////////////////////////////////////////////////////////////////
var _renameCollection = function(oldName, newName) {
db._executeTransaction({
collections: {
write: "_graphs"
},
action: function(params) {
var gdb = getGraphCollection();
if (! gdb) {
return;
}
gdb.toArray().forEach(function(doc) {
var c = _.extend({}, doc), i, j, changed = false;
if (c.edgeDefinitions) {
for (i = 0; i < c.edgeDefinitions.length; ++i) {
var def = c.edgeDefinitions[i];
if (def.collection === params.oldName) {
c.edgeDefinitions[i].collection = params.newName;
changed = true;
}
for (j = 0; j < def.from.length; ++j) {
if (def.from[j] === params.oldName) {
c.edgeDefinitions[i].from[j] = params.newName;
changed = true;
}
}
for (j = 0; j < def.to.length; ++j) {
if (def.to[j] === params.oldName) {
c.edgeDefinitions[i].to[j] = params.newName;
changed = true;
}
}
}
}
for (i = 0; i < c.orphanCollections.length; ++i) {
if (c.orphanCollections[i] === params.oldName) {
c.orphanCollections[i] = params.newName;
changed = true;
}
}
if (changed) {
gdb.update(doc._key, c);
}
});
},
params: {
oldName: oldName,
newName: newName
}
});
};
////////////////////////////////////////////////////////////////////////////////
/// @brief Helper for dropping collections of a graph.
////////////////////////////////////////////////////////////////////////////////
var checkIfMayBeDropped = function(colName, graphName, graphs) {
var result = true;
graphs.forEach(
function(graph) {
if (graph._key === graphName) {
return;
}
var edgeDefinitions = graph.edgeDefinitions;
if (edgeDefinitions) {
edgeDefinitions.forEach(
function(edgeDefinition) {
var from = edgeDefinition.from;
var to = edgeDefinition.to;
var collection = edgeDefinition.collection;
if (collection === colName
|| from.indexOf(colName) !== -1
|| to.indexOf(colName) !== -1
) {
result = false;
}
}
);
}
var orphanCollections = graph.orphanCollections;
if (orphanCollections) {
if (orphanCollections.indexOf(colName) !== -1) {
result = false;
}
}
}
);
return result;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_drop
////////////////////////////////////////////////////////////////////////////////
var _drop = function(graphId, dropCollections) {
var gdb = getGraphCollection(),
graphs;
if (!gdb.exists(graphId)) {
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_NOT_FOUND.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_NOT_FOUND.message;
throw err;
}
if (dropCollections === true) {
var graph = gdb.document(graphId);
var edgeDefinitions = graph.edgeDefinitions;
edgeDefinitions.forEach(
function(edgeDefinition) {
var from = edgeDefinition.from;
var to = edgeDefinition.to;
var collection = edgeDefinition.collection;
graphs = getGraphCollection().toArray();
if (checkIfMayBeDropped(collection, graph._key, graphs)) {
db._drop(collection);
}
from.forEach(
function(col) {
if (checkIfMayBeDropped(col, graph._key, graphs)) {
db._drop(col);
}
}
);
to.forEach(
function(col) {
if (checkIfMayBeDropped(col, graph._key, graphs)) {
db._drop(col);
}
}
);
}
);
//drop orphans
graphs = getGraphCollection().toArray();
if (!graph.orphanCollections) {
graph.orphanCollections = [];
}
graph.orphanCollections.forEach(
function(oC) {
if (checkIfMayBeDropped(oC, graph._key, graphs)) {
try {
db._drop(oC);
} catch (ignore) {}
}
}
);
}
gdb.remove(graphId);
return true;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief return all edge collections of the graph.
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._edgeCollections = function() {
return _.values(this.__edgeCollections);
};
////////////////////////////////////////////////////////////////////////////////
/// @brief return all vertex collections of the graph.
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._vertexCollections = function() {
var orphans = [];
_.each(this.__orphanCollections, function(o) {
orphans.push(db[o]);
});
return _.union(_.values(this.__vertexCollections), orphans);
};
////////////////////////////////////////////////////////////////////////////////
/// @brief _EDGES(vertexId).
////////////////////////////////////////////////////////////////////////////////
// might be needed from AQL itself
Graph.prototype._EDGES = function(vertexId) {
var err;
if (vertexId.indexOf("/") === -1) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_NOT_FOUND.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_NOT_FOUND.message + ": " + vertexId;
throw err;
}
var result = [], c;
for (c in this.__edgeCollections) {
if (this.__edgeCollections.hasOwnProperty(c)) {
if (this.__useBuiltIn) {
result = result.concat(this.__edgeCollections[c].EDGES(vertexId));
}
else {
result = result.concat(this.__edgeCollections[c].edges(vertexId));
}
}
}
return result;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief INEDGES(vertexId).
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._INEDGES = function(vertexId) {
var err;
if (vertexId.indexOf("/") === -1) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_NOT_FOUND.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_NOT_FOUND.message + ": " + vertexId;
throw err;
}
var result = [], c;
for (c in this.__edgeCollections) {
if (this.__edgeCollections.hasOwnProperty(c)) {
if (this.__useBuiltIn) {
result = result.concat(this.__edgeCollections[c].INEDGES(vertexId));
}
else {
result = result.concat(this.__edgeCollections[c].inEdges(vertexId));
}
}
}
return result;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief outEdges(vertexId).
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._OUTEDGES = function(vertexId) {
var err;
if (vertexId.indexOf("/") === -1) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_NOT_FOUND.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_NOT_FOUND.message + ": " + vertexId;
throw err;
}
var result = [], c;
for (c in this.__edgeCollections) {
if (this.__edgeCollections.hasOwnProperty(c)) {
if (this.__useBuiltIn) {
result = result.concat(this.__edgeCollections[c].OUTEDGES(vertexId));
}
else {
result = result.concat(this.__edgeCollections[c].outEdges(vertexId));
}
}
}
return result;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_edges
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._edges = function(edgeExample) {
var AQLStmt = new AQLGenerator(this);
// If no direction is specified all edges are duplicated.
// => For initial requests a direction has to be set
return AQLStmt.outEdges(edgeExample);
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_vertices
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._vertices = function(example) {
var AQLStmt = new AQLGenerator(this);
return AQLStmt.vertices(example);
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_fromVertex
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._fromVertex = function(edgeId) {
if (typeof edgeId !== 'string' ||
edgeId.indexOf('/') === -1) {
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.code;
err.errorMessage = arangodb.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.message;
throw err;
}
var edgeCollection = this._getEdgeCollectionByName(edgeId.split("/")[0]);
var document = edgeCollection.document(edgeId);
if (document) {
var vertexId = document._from;
var vertexCollection = this._getVertexCollectionByName(vertexId.split("/")[0]);
return vertexCollection.document(vertexId);
}
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_toVertex
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._toVertex = function(edgeId) {
if (typeof edgeId !== 'string' ||
edgeId.indexOf('/') === -1) {
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.code;
err.errorMessage = arangodb.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.message;
throw err;
}
var edgeCollection = this._getEdgeCollectionByName(edgeId.split("/")[0]);
var document = edgeCollection.document(edgeId);
if (document) {
var vertexId = document._to;
var vertexCollection = this._getVertexCollectionByName(vertexId.split("/")[0]);
return vertexCollection.document(vertexId);
}
};
////////////////////////////////////////////////////////////////////////////////
/// @brief get edge collection by name.
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._getEdgeCollectionByName = function(name) {
if (this.__edgeCollections[name]) {
return this.__edgeCollections[name];
}
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_EDGE_COL_DOES_NOT_EXIST.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_EDGE_COL_DOES_NOT_EXIST.message + ": " + name;
throw err;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief get vertex collection by name.
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._getVertexCollectionByName = function(name) {
if (this.__vertexCollections[name]) {
return this.__vertexCollections[name];
}
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.message + ": " + name;
throw err;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_neighbors
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._neighbors = function(vertexExample, options) {
var AQLStmt = new AQLGenerator(this);
// If no direction is specified all edges are duplicated.
// => For initial requests a direction has to be set
if (!options) {
options = {};
}
return AQLStmt.vertices(vertexExample).neighbors(options.neighborExamples, options)
.toArray();
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_common_neighbors
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._commonNeighbors = function(vertex1Example, vertex2Example, optionsVertex1, optionsVertex2) {
var ex1 = transformExample(vertex1Example);
var ex2 = transformExample(vertex2Example);
var query = "FOR e"
+ " IN GRAPH_COMMON_NEIGHBORS(@graphName"
+ ',@ex1'
+ ',@ex2'
+ ',@options1'
+ ',@options2'
+ ') RETURN e';
optionsVertex1 = optionsVertex1 || {};
optionsVertex2 = optionsVertex2 || {};
var bindVars = {
"graphName": this.__name,
"options1": optionsVertex1,
"options2": optionsVertex2,
"ex1": ex1,
"ex2": ex2
};
return db._query(query, bindVars, {count: true}).toArray();
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_count_common_neighbors
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._countCommonNeighbors = function(vertex1Example, vertex2Example, optionsVertex1, optionsVertex2) {
var ex1 = transformExample(vertex1Example);
var ex2 = transformExample(vertex2Example);
var query = "FOR e"
+ " IN GRAPH_COMMON_NEIGHBORS(@graphName"
+ ',@ex1'
+ ',@ex2'
+ ',@options1'
+ ',@options2'
+ ') RETURN [e.left, e.right, LENGTH(e.neighbors)]';
optionsVertex1 = optionsVertex1 || {};
optionsVertex2 = optionsVertex2 || {};
var bindVars = {
"graphName": this.__name,
"options1": optionsVertex1,
"options2": optionsVertex2,
"ex1": ex1,
"ex2": ex2
};
var result = db._query(query, bindVars, {count: true}).toArray(),
tmp = {}, tmp2={}, returnHash = [];
result.forEach(function (r) {
if (!tmp[r[0]]) {
tmp[r[0]] = [];
}
tmp2 = {};
tmp2[r[1]] = r[2];
tmp[r[0]].push(tmp2);
});
Object.keys(tmp).forEach(function(w) {
tmp2 = {};
tmp2[w] = tmp[w];
returnHash.push(tmp2);
});
return returnHash;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_common_properties
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._commonProperties = function(vertex1Example, vertex2Example, options) {
var ex1 = transformExample(vertex1Example);
var ex2 = transformExample(vertex2Example);
var query = "FOR e"
+ " IN GRAPH_COMMON_PROPERTIES(@graphName"
+ ',@ex1'
+ ',@ex2'
+ ',@options'
+ ') SORT ATTRIBUTES(e)[0] RETURN e';
options = options || {};
var bindVars = {
"graphName": this.__name,
"options": options,
"ex1": ex1,
"ex2": ex2
};
return db._query(query, bindVars, {count: true}).toArray();
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_count_common_properties
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._countCommonProperties = function(vertex1Example, vertex2Example, options) {
var ex1 = transformExample(vertex1Example);
var ex2 = transformExample(vertex2Example);
var query = "FOR e"
+ " IN GRAPH_COMMON_PROPERTIES(@graphName"
+ ',@ex1'
+ ',@ex2'
+ ',@options'
+ ') FOR a in ATTRIBUTES(e) SORT ATTRIBUTES(e)[0] RETURN [ ATTRIBUTES(e)[0], LENGTH(e[a]) ]';
options = options || {};
var bindVars = {
"graphName": this.__name,
"options": options,
"ex1": ex1,
"ex2": ex2
};
var result = db._query(query, bindVars, {count: true}).toArray(), returnHash = [];
result.forEach(function (r) {
var tmp = {};
tmp[r[0]] = r[1];
returnHash.push(tmp);
});
return returnHash;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_paths
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._paths = function(options) {
var query = "RETURN"
+ " GRAPH_PATHS(@graphName"
+ ',@options'
+ ')';
options = options || {};
var bindVars = {
"graphName": this.__name,
"options": options
};
var result = db._query(query, bindVars).toArray();
return result;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_shortest_path
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._shortestPath = function(startVertexExample, endVertexExample, options) {
var ex1 = transformExample(startVertexExample);
var ex2 = transformExample(endVertexExample);
var query = "RETURN"
+ " GRAPH_SHORTEST_PATH(@graphName"
+ ',@ex1'
+ ',@ex2'
+ ',@options'
+ ')';
options = options || {};
var bindVars = {
"graphName": this.__name,
"options": options,
"ex1": ex1,
"ex2": ex2
};
var result = db._query(query, bindVars).toArray();
return result;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_distance_to
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._distanceTo = function(startVertexExample, endVertexExample, options) {
var ex1 = transformExample(startVertexExample);
var ex2 = transformExample(endVertexExample);
var query = "RETURN"
+ " GRAPH_DISTANCE_TO(@graphName"
+ ',@ex1'
+ ',@ex2'
+ ',@options'
+ ')';
options = options || {};
var bindVars = {
"graphName": this.__name,
"options": options,
"ex1": ex1,
"ex2": ex2
};
var result = db._query(query, bindVars).toArray();
return result[0];
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_absolute_eccentricity
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._absoluteEccentricity = function(vertexExample, options) {
var ex1 = transformExample(vertexExample);
var query = "RETURN"
+ " GRAPH_ABSOLUTE_ECCENTRICITY(@graphName"
+ ',@ex1'
+ ',@options'
+ ')';
options = options || {};
var bindVars = {
"graphName": this.__name,
"options": options,
"ex1": ex1
};
var result = db._query(query, bindVars).toArray();
if (result.length === 1) {
return result[0];
}
return result;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_eccentricity
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._eccentricity = function(options) {
var query = "RETURN"
+ " GRAPH_ECCENTRICITY(@graphName"
+ ',@options'
+ ')';
options = options || {};
var bindVars = {
"graphName": this.__name,
"options": options
};
var result = db._query(query, bindVars).toArray();
if (result.length === 1) {
return result[0];
}
return result;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_absolute_closeness
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._absoluteCloseness = function(vertexExample, options) {
var ex1 = transformExample(vertexExample);
var query = "RETURN"
+ " GRAPH_ABSOLUTE_CLOSENESS(@graphName"
+ ',@ex1'
+ ',@options'
+ ')';
options = options || {};
var bindVars = {
"graphName": this.__name,
"options": options,
"ex1": ex1
};
var result = db._query(query, bindVars).toArray();
if (result.length === 1) {
return result[0];
}
return result;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_closeness
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._closeness = function(options) {
var query = "RETURN"
+ " GRAPH_CLOSENESS(@graphName"
+ ',@options'
+ ')';
options = options || {};
var bindVars = {
"graphName": this.__name,
"options": options
};
var result = db._query(query, bindVars).toArray();
if (result.length === 1) {
return result[0];
}
return result;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_absolute_betweenness
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._absoluteBetweenness = function(example, options) {
var query = "RETURN"
+ " GRAPH_ABSOLUTE_BETWEENNESS(@graphName"
+ ",@example"
+ ",@options"
+ ")";
options = options || {};
var bindVars = {
"example": example,
"graphName": this.__name,
"options": options
};
var result = db._query(query, bindVars).toArray();
if (result.length === 1) {
return result[0];
}
return result;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_betweenness
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._betweenness = function(options) {
var query = "RETURN"
+ " GRAPH_BETWEENNESS(@graphName"
+ ',@options'
+ ')';
options = options || {};
var bindVars = {
"graphName": this.__name,
"options": options
};
var result = db._query(query, bindVars).toArray();
if (result.length === 1) {
return result[0];
}
return result;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_radius
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._radius = function(options) {
var query = "RETURN"
+ " GRAPH_RADIUS(@graphName"
+ ',@options'
+ ')';
options = options || {};
var bindVars = {
"graphName": this.__name,
"options": options
};
var result = db._query(query, bindVars).toArray();
if (result.length === 1) {
return result[0];
}
return result;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_diameter
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._diameter = function(options) {
var query = "RETURN"
+ " GRAPH_DIAMETER(@graphName"
+ ',@options'
+ ')';
options = options || {};
var bindVars = {
"graphName": this.__name,
"options": options
};
var result = db._query(query, bindVars).toArray();
if (result.length === 1) {
return result[0];
}
return result;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph__extendEdgeDefinitions
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._extendEdgeDefinitions = function(edgeDefinition) {
edgeDefinition = sortEdgeDefinition(edgeDefinition);
var self = this;
var err;
//check if edgeCollection not already used
var eC = edgeDefinition.collection;
// ... in same graph
if (this.__edgeCollections[eC] !== undefined) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_COLLECTION_MULTI_USE.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_COLLECTION_MULTI_USE.message;
throw err;
}
//in different graph
db._graphs.toArray().forEach(
function(singleGraph) {
var sGEDs = singleGraph.edgeDefinitions;
sGEDs.forEach(
function(sGED) {
var col = sGED.collection;
if (col === eC) {
if (JSON.stringify(sGED) !== JSON.stringify(edgeDefinition)) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_COLLECTION_USE_IN_MULTI_GRAPHS.code;
err.errorMessage = col
+ " " + arangodb.errors.ERROR_GRAPH_COLLECTION_USE_IN_MULTI_GRAPHS.message;
throw err;
}
}
}
);
}
);
findOrCreateCollectionsByEdgeDefinitions([edgeDefinition]);
this.__edgeDefinitions.push(edgeDefinition);
db._graphs.update(this.__name, {edgeDefinitions: this.__edgeDefinitions});
this.__edgeCollections[edgeDefinition.collection] = db[edgeDefinition.collection];
edgeDefinition.from.forEach(
function(vc) {
self[vc] = db[vc];
//remove from __orphanCollections
var orphanIndex = self.__orphanCollections.indexOf(vc);
if (orphanIndex !== -1) {
self.__orphanCollections.splice(orphanIndex, 1);
}
//push into __vertexCollections
if (self.__vertexCollections[vc] === undefined) {
self.__vertexCollections[vc] = db[vc];
}
}
);
edgeDefinition.to.forEach(
function(vc) {
self[vc] = db[vc];
//remove from __orphanCollections
var orphanIndex = self.__orphanCollections.indexOf(vc);
if (orphanIndex !== -1) {
self.__orphanCollections.splice(orphanIndex, 1);
}
//push into __vertexCollections
if (self.__vertexCollections[vc] === undefined) {
self.__vertexCollections[vc] = db[vc];
}
}
);
updateBindCollections(this);
};
////////////////////////////////////////////////////////////////////////////////
/// @brief internal function for editing edge definitions
////////////////////////////////////////////////////////////////////////////////
var changeEdgeDefinitionsForGraph = function(graph, edgeDefinition, newCollections, possibleOrphans, self) {
var graphCollections = [];
var graphObj = _graph(graph._key);
var eDs = graph.edgeDefinitions;
var gotAHit = false;
//replace edgeDefintion
eDs.forEach(
function(eD, id) {
if(eD.collection === edgeDefinition.collection) {
gotAHit = true;
eDs[id].from = edgeDefinition.from;
eDs[id].to = edgeDefinition.to;
db._graphs.update(graph._key, {edgeDefinitions: eDs});
if (graph._key === self.__name) {
self.__edgeDefinitions[id].from = edgeDefinition.from;
self.__edgeDefinitions[id].to = edgeDefinition.to;
}
} else {
//collect all used collections
graphCollections = _.union(graphCollections, eD.from);
graphCollections = _.union(graphCollections, eD.to);
}
}
);
if (!gotAHit) {
return;
}
//remove used collection from orphanage
if (graph._key === self.__name) {
newCollections.forEach(
function(nc) {
if (self.__vertexCollections[nc] === undefined) {
self.__vertexCollections[nc] = db[nc];
}
try {
self._removeVertexCollection(nc, false);
} catch (ignore) { }
}
);
possibleOrphans.forEach(
function(po) {
if (graphCollections.indexOf(po) === -1) {
delete self.__vertexCollections[po];
self._addVertexCollection(po);
}
}
);
} else {
newCollections.forEach(
function(nc) {
try {
graphObj._removeVertexCollection(nc, false);
} catch (ignore) { }
}
);
possibleOrphans.forEach(
function(po) {
if (graphCollections.indexOf(po) === -1) {
delete graphObj.__vertexCollections[po];
graphObj._addVertexCollection(po);
}
}
);
}
//move unused collections to orphanage
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph__editEdgeDefinition
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._editEdgeDefinitions = function(edgeDefinition) {
edgeDefinition = sortEdgeDefinition(edgeDefinition);
var self = this;
//check, if in graphs edge definition
if (this.__edgeCollections[edgeDefinition.collection] === undefined) {
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_EDGE_COLLECTION_NOT_USED.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_EDGE_COLLECTION_NOT_USED.message;
throw err;
}
findOrCreateCollectionsByEdgeDefinitions([edgeDefinition]);
//evaluate collections to add to orphanage
var possibleOrphans = [];
var currentEdgeDefinition;
this.__edgeDefinitions.forEach(
function(ed) {
if (edgeDefinition.collection === ed.collection) {
currentEdgeDefinition = ed;
}
}
);
var currentCollections = _.union(currentEdgeDefinition.from, currentEdgeDefinition.to);
var newCollections = _.union(edgeDefinition.from, edgeDefinition.to);
currentCollections.forEach(
function(colName) {
if (newCollections.indexOf(colName) === -1) {
possibleOrphans.push(colName);
}
}
);
//change definition for ALL graphs
var graphs = getGraphCollection().toArray();
graphs.forEach(
function(graph) {
changeEdgeDefinitionsForGraph(graph, edgeDefinition, newCollections, possibleOrphans, self);
}
);
updateBindCollections(this);
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph__deleteEdgeDefinition
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._deleteEdgeDefinition = function(edgeCollection, dropCollection) {
//check, if in graphs edge definition
if (this.__edgeCollections[edgeCollection] === undefined) {
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_EDGE_COLLECTION_NOT_USED.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_EDGE_COLLECTION_NOT_USED.message;
throw err;
}
var edgeDefinitions = this.__edgeDefinitions,
self = this,
usedVertexCollections = [],
possibleOrphans = [],
index;
edgeDefinitions.forEach(
function(edgeDefinition, idx) {
if (edgeDefinition.collection === edgeCollection) {
index = idx;
possibleOrphans = edgeDefinition.from;
possibleOrphans = _.union(possibleOrphans, edgeDefinition.to);
} else {
usedVertexCollections = _.union(usedVertexCollections, edgeDefinition.from);
usedVertexCollections = _.union(usedVertexCollections, edgeDefinition.to);
}
}
);
this.__edgeDefinitions.splice(index, 1);
possibleOrphans.forEach(
function(po) {
if (usedVertexCollections.indexOf(po) === -1) {
self.__orphanCollections.push(po);
}
}
);
updateBindCollections(this);
db._graphs.update(
this.__name,
{
orphanCollections: this.__orphanCollections,
edgeDefinitions: this.__edgeDefinitions
}
);
if (dropCollection) {
db._drop(edgeCollection);
}
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph__addVertexCollection
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._addVertexCollection = function(vertexCollectionName, createCollection) {
//check edgeCollection
var ec = db._collection(vertexCollectionName);
var err;
if (ec === null) {
if (createCollection !== false) {
db._create(vertexCollectionName);
} else {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.code;
err.errorMessage = vertexCollectionName + arangodb.errors.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.message;
throw err;
}
} else if (ec.type() !== 2) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_WRONG_COLLECTION_TYPE_VERTEX.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_WRONG_COLLECTION_TYPE_VERTEX.message;
throw err;
}
if (this.__vertexCollections[vertexCollectionName] !== undefined) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_COLLECTION_USED_IN_EDGE_DEF.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_COLLECTION_USED_IN_EDGE_DEF.message;
throw err;
}
if (_.contains(this.__orphanCollections, vertexCollectionName)) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_COLLECTION_USED_IN_ORPHANS.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_COLLECTION_USED_IN_ORPHANS.message;
throw err;
}
this.__orphanCollections.push(vertexCollectionName);
updateBindCollections(this);
db._graphs.update(this.__name, {orphanCollections: this.__orphanCollections});
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph__orphanCollections
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._orphanCollections = function() {
return this.__orphanCollections;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph__removeVertexCollection
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._removeVertexCollection = function(vertexCollectionName, dropCollection) {
var err;
if (db._collection(vertexCollectionName) === null) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.message;
throw err;
}
var index = this.__orphanCollections.indexOf(vertexCollectionName);
if (index === -1) {
err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_GRAPH_NOT_IN_ORPHAN_COLLECTION.code;
err.errorMessage = arangodb.errors.ERROR_GRAPH_NOT_IN_ORPHAN_COLLECTION.message;
throw err;
}
this.__orphanCollections.splice(index, 1);
delete this[vertexCollectionName];
db._graphs.update(this.__name, {orphanCollections: this.__orphanCollections});
if (dropCollection === true) {
var graphs = getGraphCollection().toArray();
if (checkIfMayBeDropped(vertexCollectionName, null, graphs)) {
db._drop(vertexCollectionName);
}
}
updateBindCollections(this);
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_connectingEdges
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._getConnectingEdges = function(vertexExample1, vertexExample2, options) {
options = options || {};
var opts = {
includeData: true
};
if (options.vertex1CollectionRestriction) {
opts.startVertexCollectionRestriction = options.vertex1CollectionRestriction;
}
if (options.vertex2CollectionRestriction) {
opts.endVertexCollectionRestriction = options.vertex2CollectionRestriction;
}
if (options.edgeCollectionRestriction) {
opts.edgeCollectionRestriction = options.edgeCollectionRestriction;
}
if (options.edgeExamples) {
opts.edgeExamples = options.edgeExamples;
}
if (vertexExample2) {
opts.neighborExamples = vertexExample2;
}
var query = "RETURN"
+ " GRAPH_EDGES(@graphName"
+ ',@vertexExample'
+ ',@options'
+ ')';
var bindVars = {
"graphName": this.__name,
"vertexExample": vertexExample1,
"options": opts
};
var result = db._query(query, bindVars).toArray();
return result[0];
};
////////////////////////////////////////////////////////////////////////////////
/// @brief print basic information for the graph
////////////////////////////////////////////////////////////////////////////////
Graph.prototype._PRINT = function(context) {
var name = this.__name;
var edgeDefs = printEdgeDefinitions(this.__edgeDefinitions);
context.output += "[ Graph ";
context.output += name;
context.output += " EdgeDefinitions: ";
internal.printRecursive(edgeDefs, context);
context.output += " VertexCollections: ";
internal.printRecursive(this.__orphanCollections, context);
context.output += " ]";
};
/// Deprecated function (announced 2.3)
exports._undirectedRelation = _undirectedRelation;
/// Deprecated function (announced 2.3)
exports._directedRelation = function () {
return _relation.apply(this, arguments);
};
exports._relation = _relation;
exports._graph = _graph;
exports._edgeDefinitions = _edgeDefinitions;
exports._extendEdgeDefinitions = _extendEdgeDefinitions;
exports._create = _create;
exports._drop = _drop;
exports._exists = _exists;
exports._renameCollection = _renameCollection;
exports._list = _list;
exports._listObjects = _listObjects;
////////////////////////////////////////////////////////////////////////////////
/// some more documentation
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_create_graph_example1
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_general_graph_create_graph_example2
////////////////////////////////////////////////////////////////////////////////