@restorecommerce/chassis-srv 0.3.3 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/lib/cache/index.js +22 -3
- package/lib/cache/index.js.map +1 -1
- package/lib/command-interface/index.d.ts +3 -3
- package/lib/command-interface/index.js +432 -498
- package/lib/command-interface/index.js.map +1 -1
- package/lib/config/index.js +6 -15
- package/lib/config/index.js.map +1 -1
- package/lib/database/index.d.ts +2 -4
- package/lib/database/index.js +4 -13
- package/lib/database/index.js.map +1 -1
- package/lib/database/provider/arango/base.js +314 -322
- package/lib/database/provider/arango/base.js.map +1 -1
- package/lib/database/provider/arango/common.js +70 -57
- package/lib/database/provider/arango/common.js.map +1 -1
- package/lib/database/provider/arango/graph.d.ts +4 -8
- package/lib/database/provider/arango/graph.js +335 -470
- package/lib/database/provider/arango/graph.js.map +1 -1
- package/lib/database/provider/arango/index.js +34 -21
- package/lib/database/provider/arango/index.js.map +1 -1
- package/lib/database/provider/arango/interface.d.ts +70 -0
- package/lib/database/provider/arango/interface.js +46 -0
- package/lib/database/provider/arango/interface.js.map +1 -0
- package/lib/database/provider/arango/utils.d.ts +77 -0
- package/lib/database/provider/arango/utils.js +587 -0
- package/lib/database/provider/arango/utils.js.map +1 -0
- package/lib/database/provider/nedb/index.js +203 -206
- package/lib/database/provider/nedb/index.js.map +1 -1
- package/lib/health/index.js +36 -42
- package/lib/health/index.js.map +1 -1
- package/lib/index.d.ts +4 -0
- package/lib/index.js +27 -6
- package/lib/index.js.map +1 -1
- package/lib/microservice/endpoint.js +33 -23
- package/lib/microservice/endpoint.js.map +1 -1
- package/lib/microservice/server.js +115 -106
- package/lib/microservice/server.js.map +1 -1
- package/lib/microservice/transport/provider/grpc/index.js +58 -52
- package/lib/microservice/transport/provider/grpc/index.js.map +1 -1
- package/lib/microservice/transport/provider/grpc/reflection.js +101 -93
- package/lib/microservice/transport/provider/grpc/reflection.js.map +1 -1
- package/lib/offsets/index.d.ts +2 -2
- package/lib/offsets/index.js +41 -42
- package/lib/offsets/index.js.map +1 -1
- package/package.json +29 -29
- package/tsconfig.json +13 -6
- package/setupTopics.js +0 -32
|
@@ -1,22 +1,35 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
+
}) : function(o, v) {
|
|
12
|
+
o["default"] = v;
|
|
13
|
+
});
|
|
14
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
+
if (mod && mod.__esModule) return mod;
|
|
16
|
+
var result = {};
|
|
17
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
+
__setModuleDefault(result, mod);
|
|
19
|
+
return result;
|
|
10
20
|
};
|
|
11
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
22
|
exports.ArangoGraph = void 0;
|
|
13
|
-
const _ = require("lodash");
|
|
23
|
+
const _ = __importStar(require("lodash"));
|
|
14
24
|
const base_1 = require("./base");
|
|
15
25
|
const common_1 = require("./common");
|
|
26
|
+
const utils_1 = require("./utils");
|
|
27
|
+
const interface_1 = require("./interface");
|
|
16
28
|
class ArangoGraph extends base_1.Arango {
|
|
17
|
-
constructor(conn, graph) {
|
|
29
|
+
constructor(conn, graph, edgeDefConfig) {
|
|
18
30
|
super(conn);
|
|
19
31
|
this.graph = graph;
|
|
32
|
+
this.edgeDefConfig = edgeDefConfig; // edge definition config
|
|
20
33
|
}
|
|
21
34
|
/**
|
|
22
35
|
* create a Graph instance.
|
|
@@ -26,29 +39,27 @@ class ArangoGraph extends base_1.Arango {
|
|
|
26
39
|
* @param options — Options for creating the graph.
|
|
27
40
|
* @return {Object} A Graph instance
|
|
28
41
|
*/
|
|
29
|
-
createGraphDB(graphName) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
throw new Error('missing graph name');
|
|
35
|
-
}
|
|
36
|
-
graph = this.db.graph(graphName);
|
|
37
|
-
try {
|
|
38
|
-
yield graph.create();
|
|
39
|
-
}
|
|
40
|
-
catch (err) {
|
|
41
|
-
if (err.message === 'graph already exists') {
|
|
42
|
-
return this.graph;
|
|
43
|
-
}
|
|
44
|
-
throw { code: err.code, message: err.message };
|
|
45
|
-
}
|
|
46
|
-
return graph;
|
|
42
|
+
async createGraphDB(graphName) {
|
|
43
|
+
if (!this.graph) {
|
|
44
|
+
let graph;
|
|
45
|
+
if (_.isNil(graphName)) {
|
|
46
|
+
throw new Error('missing graph name');
|
|
47
47
|
}
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
graph = this.db.graph(graphName);
|
|
49
|
+
try {
|
|
50
|
+
await graph.create();
|
|
50
51
|
}
|
|
51
|
-
|
|
52
|
+
catch (err) {
|
|
53
|
+
if (err.message === 'graph already exists') {
|
|
54
|
+
return this.graph;
|
|
55
|
+
}
|
|
56
|
+
throw { code: err.code, message: err.message };
|
|
57
|
+
}
|
|
58
|
+
return graph;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
return this.graph;
|
|
62
|
+
}
|
|
52
63
|
}
|
|
53
64
|
/**
|
|
54
65
|
* create a new Vertex with given data.
|
|
@@ -57,41 +68,39 @@ class ArangoGraph extends base_1.Arango {
|
|
|
57
68
|
* @param {Object} data data for vertex
|
|
58
69
|
* @return {Object} created vertex
|
|
59
70
|
*/
|
|
60
|
-
createVertex(collectionName, data) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
responseDocs.push(eachDoc);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
catch (e) {
|
|
86
|
-
responseDocs.push({
|
|
87
|
-
error: true,
|
|
88
|
-
errorNum: e.code,
|
|
89
|
-
errorMessage: e.message
|
|
90
|
-
});
|
|
71
|
+
async createVertex(collectionName, data) {
|
|
72
|
+
if (_.isNil(collectionName)) {
|
|
73
|
+
throw new Error('missing vertex collection name');
|
|
74
|
+
}
|
|
75
|
+
if (_.isNil(data)) {
|
|
76
|
+
throw new Error('missing data for vertex');
|
|
77
|
+
}
|
|
78
|
+
const collection = this.graph.vertexCollection(collectionName);
|
|
79
|
+
let docs = _.cloneDeep(data);
|
|
80
|
+
if (!_.isArray(docs)) {
|
|
81
|
+
docs = [docs];
|
|
82
|
+
}
|
|
83
|
+
_.forEach(docs, (document, i) => {
|
|
84
|
+
docs[i] = (0, common_1.sanitizeInputFields)(document);
|
|
85
|
+
});
|
|
86
|
+
let responseDocs = [];
|
|
87
|
+
for (let eachDoc of docs) {
|
|
88
|
+
let result;
|
|
89
|
+
try {
|
|
90
|
+
result = await collection.save(eachDoc);
|
|
91
|
+
if (!result.error) {
|
|
92
|
+
responseDocs.push(eachDoc);
|
|
91
93
|
}
|
|
92
94
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
+
catch (e) {
|
|
96
|
+
responseDocs.push({
|
|
97
|
+
error: true,
|
|
98
|
+
errorNum: e.code,
|
|
99
|
+
errorMessage: e.message
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return _.map(responseDocs, common_1.sanitizeOutputFields);
|
|
95
104
|
}
|
|
96
105
|
/**
|
|
97
106
|
* Retreives the vertex with the given documentHandle from the collection.
|
|
@@ -102,18 +111,16 @@ class ArangoGraph extends base_1.Arango {
|
|
|
102
111
|
* or a vertex (i.e. an object with an _id or _key property).
|
|
103
112
|
* @return {Object} created vertex
|
|
104
113
|
*/
|
|
105
|
-
getVertex(collectionName, documentHandle) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return doc;
|
|
116
|
-
});
|
|
114
|
+
async getVertex(collectionName, documentHandle) {
|
|
115
|
+
if (_.isNil(collectionName)) {
|
|
116
|
+
throw new Error('missing vertex collection name');
|
|
117
|
+
}
|
|
118
|
+
if (_.isNil(documentHandle)) {
|
|
119
|
+
throw new Error('missing document handle');
|
|
120
|
+
}
|
|
121
|
+
const collection = this.graph.vertexCollection(collectionName);
|
|
122
|
+
const doc = await collection.vertex(documentHandle);
|
|
123
|
+
return doc;
|
|
117
124
|
}
|
|
118
125
|
/**
|
|
119
126
|
* Deletes the vertex with the given documentHandle from the collection.
|
|
@@ -124,28 +131,26 @@ class ArangoGraph extends base_1.Arango {
|
|
|
124
131
|
* or a vertex (i.e. an object with an _id or _key property).
|
|
125
132
|
* @return {Object} removed vertex
|
|
126
133
|
*/
|
|
127
|
-
removeVertex(collectionName, documentHandles) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
removedVertexList.push({ _id: documentHandle, _key: id, _rev: id });
|
|
145
|
-
}
|
|
134
|
+
async removeVertex(collectionName, documentHandles) {
|
|
135
|
+
if (_.isNil(collectionName)) {
|
|
136
|
+
throw new Error('missing vertex collection name');
|
|
137
|
+
}
|
|
138
|
+
if (_.isNil(documentHandles)) {
|
|
139
|
+
throw new Error('missing document handle property');
|
|
140
|
+
}
|
|
141
|
+
if (!_.isArray(documentHandles)) {
|
|
142
|
+
documentHandles = [documentHandles];
|
|
143
|
+
}
|
|
144
|
+
const collection = this.graph.vertexCollection(collectionName);
|
|
145
|
+
let removedVertexList = [];
|
|
146
|
+
for (let documentHandle of documentHandles) {
|
|
147
|
+
const id = documentHandle.split('/')[1];
|
|
148
|
+
let removed = await collection.remove(documentHandle);
|
|
149
|
+
if (!removed.error) {
|
|
150
|
+
removedVertexList.push({ _id: documentHandle, _key: id, _rev: id });
|
|
146
151
|
}
|
|
147
|
-
|
|
148
|
-
|
|
152
|
+
}
|
|
153
|
+
return removedVertexList;
|
|
149
154
|
}
|
|
150
155
|
/**
|
|
151
156
|
* gets a new GraphVertexCollection instance with the given name for this graph.
|
|
@@ -155,14 +160,12 @@ class ArangoGraph extends base_1.Arango {
|
|
|
155
160
|
* or a vertex (i.e. an object with an _id or _key property).
|
|
156
161
|
* @return {Object} created vertex
|
|
157
162
|
*/
|
|
158
|
-
getVertexCollection(collectionName) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
return collection;
|
|
165
|
-
});
|
|
163
|
+
async getVertexCollection(collectionName) {
|
|
164
|
+
if (_.isNil(collectionName)) {
|
|
165
|
+
throw new Error('missing vertex collection name');
|
|
166
|
+
}
|
|
167
|
+
const collection = await this.graph.vertexCollection(collectionName);
|
|
168
|
+
return collection;
|
|
166
169
|
}
|
|
167
170
|
/**
|
|
168
171
|
* Fetches all vertex collections from the graph and returns
|
|
@@ -170,11 +173,9 @@ class ArangoGraph extends base_1.Arango {
|
|
|
170
173
|
*
|
|
171
174
|
* @return {Array<Object>} vertex list
|
|
172
175
|
*/
|
|
173
|
-
listVertexCollections() {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
return collections;
|
|
177
|
-
});
|
|
176
|
+
async listVertexCollections() {
|
|
177
|
+
const collections = await this.graph.listVertexCollections();
|
|
178
|
+
return collections;
|
|
178
179
|
}
|
|
179
180
|
/**
|
|
180
181
|
* Fetches all vertex collections from the database and returns an array
|
|
@@ -182,11 +183,9 @@ class ArangoGraph extends base_1.Arango {
|
|
|
182
183
|
*
|
|
183
184
|
* @return {Array<Object>} vertex list
|
|
184
185
|
*/
|
|
185
|
-
getAllVertexCollections() {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
return collections;
|
|
189
|
-
});
|
|
186
|
+
async getAllVertexCollections() {
|
|
187
|
+
const collections = await this.graph.vertexCollections();
|
|
188
|
+
return collections;
|
|
190
189
|
}
|
|
191
190
|
/**
|
|
192
191
|
* Adds the collection with the given collectionName to the graph's
|
|
@@ -196,23 +195,21 @@ class ArangoGraph extends base_1.Arango {
|
|
|
196
195
|
* @param {boolean} excludeOrphans Whether orphan collections should be excluded.
|
|
197
196
|
* @return {Array<Object>} vertex list
|
|
198
197
|
*/
|
|
199
|
-
addVertexCollection(collectionName) {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
return collection;
|
|
211
|
-
}
|
|
212
|
-
throw new Error(err.message);
|
|
198
|
+
async addVertexCollection(collectionName) {
|
|
199
|
+
if (_.isNil(collectionName)) {
|
|
200
|
+
throw new Error('missing vertex collection name');
|
|
201
|
+
}
|
|
202
|
+
let collection;
|
|
203
|
+
try {
|
|
204
|
+
collection = await this.graph.addVertexCollection(collectionName);
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
if (err.message.indexOf('collection already used in edge def') > -1 || err.message.indexOf('collection used in orphans') > -1) {
|
|
208
|
+
return collection;
|
|
213
209
|
}
|
|
214
|
-
|
|
215
|
-
}
|
|
210
|
+
throw new Error(err.message);
|
|
211
|
+
}
|
|
212
|
+
return collection;
|
|
216
213
|
}
|
|
217
214
|
/**
|
|
218
215
|
* Removes the vertex collection with the given collectionName from the graph.
|
|
@@ -222,17 +219,15 @@ class ArangoGraph extends base_1.Arango {
|
|
|
222
219
|
* also be deleted from the database.
|
|
223
220
|
* @return {Object } removed vertex
|
|
224
221
|
*/
|
|
225
|
-
removeVertexCollection(collectionName, dropCollection) {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
return collection;
|
|
235
|
-
});
|
|
222
|
+
async removeVertexCollection(collectionName, dropCollection) {
|
|
223
|
+
if (_.isNil(collectionName)) {
|
|
224
|
+
throw new Error('missing vertex collection name');
|
|
225
|
+
}
|
|
226
|
+
if (_.isNil(dropCollection)) {
|
|
227
|
+
dropCollection = false;
|
|
228
|
+
}
|
|
229
|
+
const collection = await this.graph.removeVertexCollection(collectionName, dropCollection);
|
|
230
|
+
return collection;
|
|
236
231
|
}
|
|
237
232
|
/**
|
|
238
233
|
* @return {Graph} A Graph instance
|
|
@@ -254,23 +249,21 @@ class ArangoGraph extends base_1.Arango {
|
|
|
254
249
|
* edge in the collection, or a document (i.e. an object with an _id or _key property).
|
|
255
250
|
* @return {Object} edge object
|
|
256
251
|
*/
|
|
257
|
-
createEdge(collectionName, data, fromId, toId) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
return collection.save(data);
|
|
273
|
-
});
|
|
252
|
+
async createEdge(collectionName, data, fromId, toId) {
|
|
253
|
+
if (_.isNil(collectionName)) {
|
|
254
|
+
throw new Error('missing edge collection name');
|
|
255
|
+
}
|
|
256
|
+
if (_.isNil(data)) {
|
|
257
|
+
data = {};
|
|
258
|
+
}
|
|
259
|
+
const collection = this.graph.edgeCollection(collectionName);
|
|
260
|
+
if (fromId) {
|
|
261
|
+
Object.assign(data, { _from: fromId });
|
|
262
|
+
}
|
|
263
|
+
if (toId) {
|
|
264
|
+
Object.assign(data, { _to: toId });
|
|
265
|
+
}
|
|
266
|
+
return collection.save(data);
|
|
274
267
|
}
|
|
275
268
|
/**
|
|
276
269
|
* Retrieves the edge with the given documentHandle from the collection.
|
|
@@ -279,17 +272,15 @@ class ArangoGraph extends base_1.Arango {
|
|
|
279
272
|
* @param {String} documentHandle edge key
|
|
280
273
|
* @return {Object} edge object
|
|
281
274
|
*/
|
|
282
|
-
getEdge(collectionName, documentHandle) {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
return collection.edge(documentHandle);
|
|
292
|
-
});
|
|
275
|
+
async getEdge(collectionName, documentHandle) {
|
|
276
|
+
if (_.isNil(collectionName)) {
|
|
277
|
+
throw new Error('missing edge collection name');
|
|
278
|
+
}
|
|
279
|
+
if (_.isNil(documentHandle)) {
|
|
280
|
+
throw new Error('missing docuemnt handle');
|
|
281
|
+
}
|
|
282
|
+
const collection = this.graph.edgeCollection(collectionName);
|
|
283
|
+
return collection.edge(documentHandle);
|
|
293
284
|
}
|
|
294
285
|
/**
|
|
295
286
|
* Retrieves a list of all edges of the document with the given documentHandle.
|
|
@@ -301,17 +292,15 @@ class ArangoGraph extends base_1.Arango {
|
|
|
301
292
|
* (i.e. an object with an _id or _key property).
|
|
302
293
|
* @return {Object} edge object
|
|
303
294
|
*/
|
|
304
|
-
getAllEdgesForVertice(collectionName, documentHandle) {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
return yield collection.edges(documentHandle);
|
|
314
|
-
});
|
|
295
|
+
async getAllEdgesForVertice(collectionName, documentHandle) {
|
|
296
|
+
if (_.isNil(collectionName)) {
|
|
297
|
+
throw new Error('missing edge collection name');
|
|
298
|
+
}
|
|
299
|
+
if (_.isNil(documentHandle)) {
|
|
300
|
+
throw new Error('missing document handle');
|
|
301
|
+
}
|
|
302
|
+
const collection = this.graph.edgeCollection(collectionName).collection;
|
|
303
|
+
return await collection.edges(documentHandle);
|
|
315
304
|
}
|
|
316
305
|
/**
|
|
317
306
|
* get all incoming edges.
|
|
@@ -320,17 +309,15 @@ class ArangoGraph extends base_1.Arango {
|
|
|
320
309
|
* @param {String} documentHandle The handle of the document
|
|
321
310
|
* @return {[Object]} list of edges
|
|
322
311
|
*/
|
|
323
|
-
getInEdges(collectionName, documentHandle) {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
return yield collection.inEdges(documentHandle);
|
|
333
|
-
});
|
|
312
|
+
async getInEdges(collectionName, documentHandle) {
|
|
313
|
+
if (_.isNil(collectionName)) {
|
|
314
|
+
throw new Error('missing edge name');
|
|
315
|
+
}
|
|
316
|
+
if (_.isNil(documentHandle)) {
|
|
317
|
+
throw new Error('missing document handle');
|
|
318
|
+
}
|
|
319
|
+
const collection = this.graph.edgeCollection(collectionName).collection;
|
|
320
|
+
return await collection.inEdges(documentHandle);
|
|
334
321
|
}
|
|
335
322
|
/**
|
|
336
323
|
* get all outgoing edges.
|
|
@@ -339,62 +326,15 @@ class ArangoGraph extends base_1.Arango {
|
|
|
339
326
|
* @param {String} documentHandle The handle of the document
|
|
340
327
|
* @return {[Object]} list of edges
|
|
341
328
|
*/
|
|
342
|
-
getOutEdges(collectionName, documentHandle) {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
throw new Error('missing edge collection name');
|
|
346
|
-
}
|
|
347
|
-
if (_.isNil(documentHandle)) {
|
|
348
|
-
throw new Error('missing document handle');
|
|
349
|
-
}
|
|
350
|
-
const collection = this.graph.edgeCollection(collectionName).collection;
|
|
351
|
-
return collection.outEdges(documentHandle);
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
traversalFilter(filterObj) {
|
|
355
|
-
let stringFilter;
|
|
356
|
-
// there could be multiple vertices
|
|
357
|
-
let condition = '';
|
|
358
|
-
for (let i = 0; i < filterObj.length; i++) {
|
|
359
|
-
// check if its last element in array
|
|
360
|
-
if (i === (filterObj.length - 1)) {
|
|
361
|
-
condition = condition + ` (vertex._id.indexOf("${filterObj[i].vertex}") > -1)`;
|
|
362
|
-
}
|
|
363
|
-
else {
|
|
364
|
-
condition = condition + ` (vertex._id.indexOf("${filterObj[i].vertex}") > -1) ||`;
|
|
365
|
-
}
|
|
329
|
+
async getOutEdges(collectionName, documentHandle) {
|
|
330
|
+
if (_.isNil(collectionName)) {
|
|
331
|
+
throw new Error('missing edge collection name');
|
|
366
332
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
}
|
|
370
|
-
traversalExpander(expanderObj) {
|
|
371
|
-
let expanderFilter;
|
|
372
|
-
// there could be multiple edges
|
|
373
|
-
let condition = '';
|
|
374
|
-
let directionVar;
|
|
375
|
-
for (let i = 0; i < expanderObj.length; i++) {
|
|
376
|
-
// check if its last element in array
|
|
377
|
-
if (i === (expanderObj.length - 1)) {
|
|
378
|
-
condition = condition + ` (e._id.indexOf("${expanderObj[i].edge}") > -1)`;
|
|
379
|
-
}
|
|
380
|
-
else {
|
|
381
|
-
condition = condition + ` (e._id.indexOf("${expanderObj[i].edge}") > -1) ||`;
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
if ((expanderObj[0].direction).toLowerCase() == 'inbound') {
|
|
385
|
-
directionVar = 'getInEdges(vertex)';
|
|
333
|
+
if (_.isNil(documentHandle)) {
|
|
334
|
+
throw new Error('missing document handle');
|
|
386
335
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
}
|
|
390
|
-
expanderFilter = `var connections = [];
|
|
391
|
-
config.datasource.${directionVar}.forEach(function (e) {
|
|
392
|
-
if( ${condition} ) {
|
|
393
|
-
connections.push({ vertex: require(\"internal\").db._document(e._to), edge: e});
|
|
394
|
-
}
|
|
395
|
-
});
|
|
396
|
-
return connections;`;
|
|
397
|
-
return expanderFilter;
|
|
336
|
+
const collection = this.graph.edgeCollection(collectionName).collection;
|
|
337
|
+
return collection.outEdges(documentHandle);
|
|
398
338
|
}
|
|
399
339
|
/**
|
|
400
340
|
* collection traversal - Performs a traversal starting from the given
|
|
@@ -409,97 +349,117 @@ class ArangoGraph extends base_1.Arango {
|
|
|
409
349
|
* opts.init, opts.expander, opts.sort
|
|
410
350
|
* @return {[Object]} edge traversal path
|
|
411
351
|
*/
|
|
412
|
-
traversal(
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
if (_.isNil(startVertex)) {
|
|
417
|
-
throw new Error('missing start vertex name');
|
|
418
|
-
}
|
|
419
|
-
if (opts.lowest_common_ancestor) {
|
|
420
|
-
return this.findTreesCommonAncestor(startVertex, collectionName, edgeName);
|
|
421
|
-
}
|
|
422
|
-
let response = {
|
|
423
|
-
vertex_fields: [],
|
|
424
|
-
data: {},
|
|
425
|
-
paths: {}
|
|
426
|
-
};
|
|
427
|
-
if (aql && aql == true) {
|
|
428
|
-
// get all the first level childrens for the start vertex
|
|
429
|
-
let result = yield this.getAllChildrenNodes(startVertex, edgeName);
|
|
430
|
-
let finalResponse = [];
|
|
431
|
-
for (let item of result._result) {
|
|
432
|
-
finalResponse.push(_.omit(item, ['_key', '_id', '_rev']));
|
|
433
|
-
}
|
|
434
|
-
response.data.value = Buffer.from(JSON.stringify(finalResponse));
|
|
435
|
-
return response;
|
|
436
|
-
}
|
|
437
|
-
const vertex = startVertex;
|
|
438
|
-
if (_.isArray(vertex)) {
|
|
439
|
-
throw new Error('Invalid number of starting vertices for traversal: ' + vertex.length);
|
|
440
|
-
}
|
|
441
|
-
for (let key in opts) {
|
|
442
|
-
if (_.isEmpty(opts[key])) {
|
|
443
|
-
delete opts[key];
|
|
444
|
-
}
|
|
352
|
+
async traversal(vertices, collection, opts, filters) {
|
|
353
|
+
if (vertices) {
|
|
354
|
+
if (_.isEmpty(vertices.collection_name) && !_.isEmpty(vertices.start_vertex_id)) {
|
|
355
|
+
throw new Error(`missing collection name for vertex id ${vertices.start_vertex_id}`);
|
|
445
356
|
}
|
|
446
|
-
if (
|
|
447
|
-
|
|
357
|
+
else if (!_.isEmpty(vertices.collection_name) && _.isEmpty(vertices.start_vertex_id)) {
|
|
358
|
+
throw new Error(`missing vertex id for collection_name ${vertices.collection_name}`);
|
|
448
359
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
360
|
+
}
|
|
361
|
+
// vertices data
|
|
362
|
+
let vertexCollectionName, startVertexIds;
|
|
363
|
+
if (vertices) {
|
|
364
|
+
vertexCollectionName = vertices.collection_name;
|
|
365
|
+
startVertexIds = vertices.start_vertex_id;
|
|
366
|
+
}
|
|
367
|
+
// collection data
|
|
368
|
+
let collectionName, limit, offset, sort;
|
|
369
|
+
if (collection) {
|
|
370
|
+
collectionName = collection.collection_name;
|
|
371
|
+
limit = collection.limit;
|
|
372
|
+
offset = collection.offset;
|
|
373
|
+
sort = collection.sort;
|
|
374
|
+
}
|
|
375
|
+
if ((_.isUndefined(startVertexIds) || _.isNil(startVertexIds) || _.isEmpty(startVertexIds)) &&
|
|
376
|
+
(_.isUndefined(collectionName) || _.isNil(collectionName) || _.isEmpty(collectionName))) {
|
|
377
|
+
throw new Error('One of the Vertices or Collection should be defined');
|
|
378
|
+
}
|
|
379
|
+
// from either vertices or collections
|
|
380
|
+
const traversalCollectionName = collectionName && !_.isEmpty(collectionName) ? collectionName : vertexCollectionName;
|
|
381
|
+
if (!opts) {
|
|
382
|
+
opts = {};
|
|
383
|
+
}
|
|
384
|
+
// make outbound traversal by default if not provided
|
|
385
|
+
if (!opts.direction || _.isEmpty(opts.direction)) {
|
|
386
|
+
opts.direction = interface_1.Direction.OUTBOUND;
|
|
387
|
+
}
|
|
388
|
+
// default options
|
|
389
|
+
let defaultOptions = { uniqueVertices: 'global', bfs: true, uniqueEdges: 'path' };
|
|
390
|
+
let filter = '';
|
|
391
|
+
let rootFilter = '';
|
|
392
|
+
let limitFilter = '';
|
|
393
|
+
let sortFilter = '';
|
|
394
|
+
// include vertices in options if specified
|
|
395
|
+
if (opts.include_vertex) {
|
|
396
|
+
defaultOptions.vertexCollections = opts.include_vertex;
|
|
397
|
+
}
|
|
398
|
+
// include edges in options if specified
|
|
399
|
+
if (opts.include_edge) {
|
|
400
|
+
defaultOptions.edgeCollections = opts.include_edge;
|
|
401
|
+
}
|
|
402
|
+
// exclude vertices
|
|
403
|
+
if (opts.exclude_vertex) {
|
|
404
|
+
for (let excludeVertex of opts.exclude_vertex) {
|
|
405
|
+
filter = filter + ` FILTER v._id NOT LIKE "${excludeVertex}%" `;
|
|
469
406
|
}
|
|
470
|
-
|
|
471
|
-
|
|
407
|
+
}
|
|
408
|
+
// exclude edges
|
|
409
|
+
if (opts.exclude_edge) {
|
|
410
|
+
for (let excludeEdge of opts.exclude_edge) {
|
|
411
|
+
filter = filter + ` FILTER e._id NOT LIKE "${excludeEdge}%" `;
|
|
472
412
|
}
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
413
|
+
}
|
|
414
|
+
const rootAndAssociationFilter = (0, utils_1.createGraphsAssociationFilter)(filters, opts.direction, traversalCollectionName, this.edgeDefConfig, filter);
|
|
415
|
+
// association fitler
|
|
416
|
+
filter = rootAndAssociationFilter.associationFilter;
|
|
417
|
+
// root filter
|
|
418
|
+
const rootEntityFilter = rootAndAssociationFilter.rootEntityFilter;
|
|
419
|
+
if (rootEntityFilter) {
|
|
420
|
+
rootFilter = (0, utils_1.buildGraphFilter)([rootEntityFilter]).q;
|
|
421
|
+
}
|
|
422
|
+
if (startVertexIds && startVertexIds.length > 0) {
|
|
423
|
+
if (rootFilter && !_.isEmpty(rootFilter)) {
|
|
424
|
+
rootFilter = ` obj.id IN ${JSON.stringify(startVertexIds)} || ${rootFilter}`;
|
|
483
425
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
traversedData.visited.paths = this.arrUnique(traversedData.visited.paths);
|
|
487
|
-
const encodedPaths = common_1.encodeMessage(traversedData.visited.paths);
|
|
488
|
-
response.paths.value = encodedPaths;
|
|
489
|
-
}
|
|
426
|
+
else {
|
|
427
|
+
rootFilter = ` obj.id IN ${JSON.stringify(startVertexIds)} `;
|
|
490
428
|
}
|
|
491
|
-
|
|
492
|
-
|
|
429
|
+
}
|
|
430
|
+
// combined root filter
|
|
431
|
+
if (rootFilter && !_.isEmpty(rootFilter)) {
|
|
432
|
+
rootFilter = `FILTER ${rootFilter}`;
|
|
433
|
+
}
|
|
434
|
+
limitFilter = (0, utils_1.buildGraphLimiter)(limit, offset);
|
|
435
|
+
if (sort) {
|
|
436
|
+
sortFilter = (0, utils_1.buildGraphSorter)(sort);
|
|
437
|
+
}
|
|
438
|
+
let rootCursor, associationCursor;
|
|
439
|
+
try {
|
|
440
|
+
defaultOptions = JSON.stringify(defaultOptions);
|
|
441
|
+
// traversal data
|
|
442
|
+
const traversalQuery = `For obj IN ${traversalCollectionName} ${rootFilter} ${limitFilter} ${sortFilter}
|
|
443
|
+
FOR v, e, p IN 1..100 ${opts.direction} obj GRAPH "${this.graph.name}"
|
|
444
|
+
OPTIONS ${defaultOptions}
|
|
445
|
+
${filter}
|
|
446
|
+
RETURN { v, e, p }`;
|
|
447
|
+
associationCursor = await this.db.query(traversalQuery);
|
|
448
|
+
const rootEntityQuery = `For obj IN ${traversalCollectionName} ${rootFilter} ${limitFilter} ${sortFilter} return obj`;
|
|
449
|
+
rootCursor = await this.db.query(rootEntityQuery);
|
|
450
|
+
}
|
|
451
|
+
catch (err) {
|
|
452
|
+
throw { code: err.code, message: err.message };
|
|
453
|
+
}
|
|
454
|
+
return { rootCursor, associationCursor };
|
|
493
455
|
}
|
|
494
|
-
getAllChildrenNodes(startVertex, edgeName) {
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
'@edge_name': edgeName
|
|
500
|
-
});
|
|
501
|
-
return result;
|
|
456
|
+
async getAllChildrenNodes(startVertex, edgeName) {
|
|
457
|
+
const queryTpl = `FOR v IN 1..1 OUTBOUND @start_vertex @@edge_name RETURN v`;
|
|
458
|
+
const result = await this.db.query(queryTpl, {
|
|
459
|
+
start_vertex: startVertex,
|
|
460
|
+
'@edge_name': edgeName
|
|
502
461
|
});
|
|
462
|
+
return result;
|
|
503
463
|
}
|
|
504
464
|
arrUnique(arr) {
|
|
505
465
|
let cleaned = [];
|
|
@@ -514,93 +474,6 @@ class ArangoGraph extends base_1.Arango {
|
|
|
514
474
|
});
|
|
515
475
|
return cleaned;
|
|
516
476
|
}
|
|
517
|
-
/**
|
|
518
|
-
* Finds the lowest common ancestor between two nodes of a tree-shaped graph and returns the subtree in that node.
|
|
519
|
-
*/
|
|
520
|
-
findTreesCommonAncestor(nodes, collectionName, edgeName) {
|
|
521
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
522
|
-
// preprocessing to get all the roots
|
|
523
|
-
const collection = this.graph.edgeCollection(edgeName).collection;
|
|
524
|
-
const roots = {};
|
|
525
|
-
for (let node of nodes) {
|
|
526
|
-
node = `${collectionName}/${node}`;
|
|
527
|
-
const result = yield collection.traversal(node, {
|
|
528
|
-
direction: 'outbound'
|
|
529
|
-
});
|
|
530
|
-
// const result = await this.db.query(`FOR v IN 1..10000 OUTBOUND @vertex GRAPH @graph FILTER "${rawFilter}" RETURN v`, { graph: this.graph.name, vertex: node });
|
|
531
|
-
if (_.isEmpty(result.visited) || _.isEmpty(result.visited.vertices)) {
|
|
532
|
-
if (!roots[node]) {
|
|
533
|
-
roots[node] = [node];
|
|
534
|
-
}
|
|
535
|
-
continue;
|
|
536
|
-
}
|
|
537
|
-
const items = result.visited.vertices;
|
|
538
|
-
const root = _.isArray(items) ? items[items.length - 1] : items;
|
|
539
|
-
if (!roots[root._id]) {
|
|
540
|
-
roots[root._id] = [node];
|
|
541
|
-
}
|
|
542
|
-
else {
|
|
543
|
-
roots[root._id].push(node);
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
const that = this;
|
|
547
|
-
const findCommonAncestor = (nodeA, nodeB) => __awaiter(this, void 0, void 0, function* () {
|
|
548
|
-
const queryTpl = `LET firstPath = (FOR v IN 1..10000
|
|
549
|
-
OUTBOUND @vertex1 GRAPH @graph RETURN v)
|
|
550
|
-
FOR v,e,p IN 1..10000 OUTBOUND @vertex2 GRAPH @graph
|
|
551
|
-
LET pos = POSITION(firstPath, v, true)
|
|
552
|
-
FILTER pos != -1
|
|
553
|
-
LIMIT 1
|
|
554
|
-
let endPath = REVERSE(p.vertices)
|
|
555
|
-
return endPath`;
|
|
556
|
-
const result = yield that.db.query(queryTpl, {
|
|
557
|
-
vertex1: nodeA,
|
|
558
|
-
vertex2: nodeB,
|
|
559
|
-
graph: that.graph.name
|
|
560
|
-
});
|
|
561
|
-
if (result.count == 0) {
|
|
562
|
-
throw new Error('Unimplemented: hierarchical resources do not share the same root');
|
|
563
|
-
}
|
|
564
|
-
const item = yield result.next();
|
|
565
|
-
return item[0];
|
|
566
|
-
});
|
|
567
|
-
const lca = (nodeA, nodeList) => __awaiter(this, void 0, void 0, function* () {
|
|
568
|
-
if (nodeList.length > 1) {
|
|
569
|
-
const slices = nodeList.slice(1, nodeList.length);
|
|
570
|
-
return lca(nodeA, lca(nodes[0], slices));
|
|
571
|
-
}
|
|
572
|
-
else {
|
|
573
|
-
const result = [yield findCommonAncestor(nodeA, nodeList[0])];
|
|
574
|
-
return result;
|
|
575
|
-
}
|
|
576
|
-
});
|
|
577
|
-
let paths = []; // the edges allow us to build the tree
|
|
578
|
-
for (let root in roots) {
|
|
579
|
-
let ancestor;
|
|
580
|
-
if (roots[root].length == 1) {
|
|
581
|
-
ancestor = root;
|
|
582
|
-
}
|
|
583
|
-
else {
|
|
584
|
-
const list = roots[root];
|
|
585
|
-
let vertex = yield lca(list[0], list.slice(1, list.length));
|
|
586
|
-
if (_.isArray(vertex)) {
|
|
587
|
-
vertex = vertex[0];
|
|
588
|
-
}
|
|
589
|
-
ancestor = vertex._id;
|
|
590
|
-
}
|
|
591
|
-
const traversal = yield collection.traversal(ancestor, {
|
|
592
|
-
direction: 'inbound',
|
|
593
|
-
});
|
|
594
|
-
const visited = traversal.visited;
|
|
595
|
-
paths = paths.concat(visited.paths);
|
|
596
|
-
}
|
|
597
|
-
return {
|
|
598
|
-
paths: {
|
|
599
|
-
value: common_1.encodeMessage(paths)
|
|
600
|
-
}
|
|
601
|
-
};
|
|
602
|
-
});
|
|
603
|
-
}
|
|
604
477
|
/**
|
|
605
478
|
* Adds the given edge definition to the graph.
|
|
606
479
|
*
|
|
@@ -609,39 +482,37 @@ class ArangoGraph extends base_1.Arango {
|
|
|
609
482
|
* @param {Object} toVertice from vertice
|
|
610
483
|
* @return {Object} The added edge definition
|
|
611
484
|
*/
|
|
612
|
-
addEdgeDefinition(edgeName, fromVertice, toVertice) {
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
return edgeDef;
|
|
641
|
-
}
|
|
642
|
-
throw { code: err.code, message: err.message };
|
|
485
|
+
async addEdgeDefinition(edgeName, fromVertice, toVertice) {
|
|
486
|
+
if (_.isNil(edgeName)) {
|
|
487
|
+
throw new Error('missing edge name');
|
|
488
|
+
}
|
|
489
|
+
if (_.isNil(fromVertice)) {
|
|
490
|
+
throw new Error('missing from vertice');
|
|
491
|
+
}
|
|
492
|
+
if (_.isNil(toVertice)) {
|
|
493
|
+
throw new Error('missing to vertice');
|
|
494
|
+
}
|
|
495
|
+
if (!_.isArray(fromVertice)) {
|
|
496
|
+
fromVertice = [fromVertice];
|
|
497
|
+
}
|
|
498
|
+
if (!_.isArray(toVertice)) {
|
|
499
|
+
toVertice = [toVertice];
|
|
500
|
+
}
|
|
501
|
+
let edgeDef;
|
|
502
|
+
try {
|
|
503
|
+
edgeDef = await this.graph.addEdgeDefinition({
|
|
504
|
+
collection: edgeName,
|
|
505
|
+
from: fromVertice,
|
|
506
|
+
to: toVertice
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
catch (err) {
|
|
510
|
+
// if edge def already exists return
|
|
511
|
+
if (err.message === `${edgeName} multi use of edge collection in edge def`) {
|
|
512
|
+
return edgeDef;
|
|
643
513
|
}
|
|
644
|
-
|
|
514
|
+
throw { code: err.code, message: err.message };
|
|
515
|
+
}
|
|
645
516
|
}
|
|
646
517
|
/**
|
|
647
518
|
* Removes the edge definition with the given definitionName form the graph.
|
|
@@ -652,23 +523,19 @@ class ArangoGraph extends base_1.Arango {
|
|
|
652
523
|
* associated with the definition will also be deleted from the database.
|
|
653
524
|
* @return {Object} replaced edge definition
|
|
654
525
|
*/
|
|
655
|
-
removeEdgeDefinition(definitionName, dropCollection) {
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
return this.graph.removeEdgeDefinition(definitionName, dropCollection);
|
|
661
|
-
});
|
|
526
|
+
async removeEdgeDefinition(definitionName, dropCollection) {
|
|
527
|
+
if (_.isNil(definitionName)) {
|
|
528
|
+
throw new Error('missing definition name');
|
|
529
|
+
}
|
|
530
|
+
return this.graph.removeEdgeDefinition(definitionName, dropCollection);
|
|
662
531
|
}
|
|
663
532
|
/**
|
|
664
533
|
* list graphs.
|
|
665
534
|
*
|
|
666
535
|
* @return {Promise<any>} list all the graphs
|
|
667
536
|
*/
|
|
668
|
-
listGraphs() {
|
|
669
|
-
return
|
|
670
|
-
return this.db.listGraphs();
|
|
671
|
-
});
|
|
537
|
+
async listGraphs() {
|
|
538
|
+
return this.db.listGraphs();
|
|
672
539
|
}
|
|
673
540
|
/**
|
|
674
541
|
* Deletes the edge with the given documentHandle from the collection.
|
|
@@ -679,17 +546,15 @@ class ArangoGraph extends base_1.Arango {
|
|
|
679
546
|
* or an edge (i.e. an object with an _id or _key property).
|
|
680
547
|
* @return {Object} removed Edge
|
|
681
548
|
*/
|
|
682
|
-
removeEdge(collectionName, documentHandle) {
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
return collection.remove(documentHandle);
|
|
692
|
-
});
|
|
549
|
+
async removeEdge(collectionName, documentHandle) {
|
|
550
|
+
if (_.isNil(collectionName)) {
|
|
551
|
+
throw new Error('missing edge collection name');
|
|
552
|
+
}
|
|
553
|
+
if (_.isNil(documentHandle)) {
|
|
554
|
+
throw new Error('missing document handle');
|
|
555
|
+
}
|
|
556
|
+
const collection = this.graph.edgeCollection(collectionName);
|
|
557
|
+
return collection.remove(documentHandle);
|
|
693
558
|
}
|
|
694
559
|
}
|
|
695
560
|
exports.ArangoGraph = ArangoGraph;
|