UNPKG

@restorecommerce/chassis-srv

Version:
555 lines 22 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ArangoGraph = void 0; const _ = __importStar(require("lodash")); const base_1 = require("./base"); const common_1 = require("./common"); const utils_1 = require("./utils"); const graph_1 = require("@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/graph"); class ArangoGraph extends base_1.Arango { constructor(db, graph, edgeDefConfig, logger) { super(db); this.graph = graph; this.edgeDefConfig = edgeDefConfig; this.logger = logger; } /** * create a Graph instance. * * @param {String} graphName graph name * @param edgeDefinitions — Definitions for the relations of the graph. * @param options — Options for creating the graph. * @return {Object} A Graph instance */ async createGraphDB(graphName, edgeDefinitions, options) { if (!this.graph) { if (_.isNil(graphName)) { throw new Error('missing graph name'); } const graph = this.db.graph(graphName); try { await graph.create(edgeDefinitions, options); } catch (err) { if (err.message === 'graph already exists') { return this.graph; } throw { code: err.code, message: err.message }; } return graph; } else { return this.graph; } } /** * create a new Vertex with given data. * * @param {string} collectionName vertex collection name * @param {Object} data data for vertex * @return {Object} created vertex */ async createVertex(collectionName, data) { if (_.isNil(collectionName)) { throw new Error('missing vertex collection name'); } if (_.isNil(data)) { throw new Error('missing data for vertex'); } const collection = this.graph.vertexCollection(collectionName); let docs = _.cloneDeep(data); if (!_.isArray(docs)) { docs = [docs]; } _.forEach(docs, (document, i) => { docs[i] = (0, common_1.sanitizeInputFields)(document); }); const responseDocs = []; for (const eachDoc of docs) { let result; try { result = await collection.save(eachDoc); if (!result.error) { responseDocs.push(eachDoc); } } catch (e) { responseDocs.push({ error: true, errorNum: e.code, errorMessage: e.message }); } } return _.map(responseDocs, common_1.sanitizeOutputFields); } /** * Retreives the vertex with the given documentHandle from the collection. * * @param {string} collectionName vertex collection name * @param {string} documentHandle The handle of the vertex to retrieve. * This can be either the _id or the _key of a vertex in the collection, * or a vertex (i.e. an object with an _id or _key property). * @return {Object} created vertex */ async getVertex(collectionName, documentHandle) { if (_.isNil(collectionName)) { throw new Error('missing vertex collection name'); } if (_.isNil(documentHandle)) { throw new Error('missing document handle'); } const collection = this.graph.vertexCollection(collectionName); const doc = await collection.vertex(documentHandle); return doc; } /** * Deletes the vertex with the given documentHandle from the collection. * * @param {string} collectionName vertex collection name * @param {string[]} documentHandles An array of the documentHandles to be removed. * This can be either the _id or the _key of a vertex in the collection, * or a vertex (i.e. an object with an _id or _key property). * @return {Object} removed vertex */ async removeVertex(collectionName, documentHandles) { if (_.isNil(collectionName)) { throw new Error('missing vertex collection name'); } if (_.isNil(documentHandles)) { throw new Error('missing document handle property'); } if (!_.isArray(documentHandles)) { documentHandles = [documentHandles]; } const collection = this.graph.vertexCollection(collectionName); const removedVertexList = []; for (const documentHandle of documentHandles) { const id = documentHandle.split('/')[1]; const removed = await collection.remove(documentHandle); if (!removed.error) { removedVertexList.push({ _id: documentHandle, _key: id, _rev: id }); } } return removedVertexList; } /** * gets a new GraphVertexCollection instance with the given name for this graph. * * @param {string} collectionName The handle of the vertex to retrieve. * This can be either the _id or the _key of a vertex in the collection, * or a vertex (i.e. an object with an _id or _key property). * @return {Object} created vertex */ async getVertexCollection(collectionName) { if (_.isNil(collectionName)) { throw new Error('missing vertex collection name'); } const collection = await this.graph.vertexCollection(collectionName); return collection; } /** * Fetches all vertex collections from the graph and returns * an array of collection descriptions. * * @return {Array<Object>} vertex list */ async listVertexCollections() { const collections = await this.graph.listVertexCollections(); return collections; } /** * Fetches all vertex collections from the database and returns an array * of GraphVertexCollection instances for the collections. * * @return {Array<Object>} vertex list */ async getAllVertexCollections() { const collections = await this.graph.vertexCollections(); return collections; } /** * Adds the collection with the given collectionName to the graph's * vertex collections. * * @param {string} collectionName Name of the vertex collection to add to the graph. * @param {boolean} excludeOrphans Whether orphan collections should be excluded. * @return {Array<Object>} vertex list */ async addVertexCollection(collectionName) { if (_.isNil(collectionName)) { throw new Error('missing vertex collection name'); } let collection; try { collection = await this.graph.addVertexCollection(collectionName); } catch (err) { if (err.message.indexOf('collection already used in edge def') > -1 || err.message.indexOf('collection used in orphans') > -1) { return collection; } throw new Error(err.message); } return collection; } /** * Removes the vertex collection with the given collectionName from the graph. * * @param {string} collectionName Name of the vertex collection to remove from the graph. * @param {boolean} dropCollection If set to true, the collection will * also be deleted from the database. * @return {Object } removed vertex */ async removeVertexCollection(collectionName, dropCollection) { if (_.isNil(collectionName)) { throw new Error('missing vertex collection name'); } if (_.isNil(dropCollection)) { dropCollection = false; } const collection = await this.graph.removeVertexCollection(collectionName, dropCollection); return collection; } /** * @return {Graph} A Graph instance */ getGraphDB() { return this.graph; } /** * Creates a new edge between the vertices fromId and toId with the given data. * * @param {string} collectionName name of the edge collection * @param {Object} data The data of the new edge. If fromId and toId are not * specified, the data needs to contain the properties _from and _to. * @param {string} fromId The handle of the start vertex of this edge. * This can be either the _id of a document in the database, the _key of an * edge in the collection, or a document (i.e. an object with an _id or _key property). * @param {string} toId The handle of the end vertex of this edge. * This can be either the _id of a document in the database, the _key of an * edge in the collection, or a document (i.e. an object with an _id or _key property). * @return {Object} edge object */ async createEdge(collectionName, data, fromId, toId) { if (_.isNil(collectionName)) { throw new Error('missing edge collection name'); } if (_.isNil(data)) { data = {}; } const collection = this.graph.edgeCollection(collectionName); if (fromId) { Object.assign(data, { _from: fromId }); } if (toId) { Object.assign(data, { _to: toId }); } return collection.save(data); } /** * Retrieves the edge with the given documentHandle from the collection. * * @param {String} collectionName collection name * @param {String} documentHandle edge key * @return {Object} edge object */ async getEdge(collectionName, documentHandle) { if (_.isNil(collectionName)) { throw new Error('missing edge collection name'); } if (_.isNil(documentHandle)) { throw new Error('missing docuemnt handle'); } const collection = this.graph.edgeCollection(collectionName); return collection.edge(documentHandle); } /** * Retrieves a list of all edges of the document with the given documentHandle. * * @param {String} collectionName edge collection name * @param {String} documentHandle The handle of the document to retrieve * the edges of. This can be either the _id of a document in the database, * the _key of an edge in the collection, or a document * (i.e. an object with an _id or _key property). * @return {Object} edge object */ async getAllEdgesForVertice(collectionName, documentHandle) { if (_.isNil(collectionName)) { throw new Error('missing edge collection name'); } if (_.isNil(documentHandle)) { throw new Error('missing document handle'); } const collection = this.graph.edgeCollection(collectionName).collection; return await collection.edges(documentHandle, {}); } /** * get all incoming edges. * * @param {String} collectionName edge collection name * @param {String} documentHandle The handle of the document * @return {[Object]} list of edges */ async getInEdges(collectionName, documentHandle) { if (_.isNil(collectionName)) { throw new Error('missing edge name'); } if (_.isNil(documentHandle)) { throw new Error('missing document handle'); } const collection = this.graph.edgeCollection(collectionName).collection; return await collection.inEdges(documentHandle, {}); } /** * get all outgoing edges. * * @param {String} collectionName edge collection name * @param {String} documentHandle The handle of the document * @return {[Object]} list of edges */ async getOutEdges(collectionName, documentHandle) { if (_.isNil(collectionName)) { throw new Error('missing edge collection name'); } if (_.isNil(documentHandle)) { throw new Error('missing document handle'); } const collection = this.graph.edgeCollection(collectionName).collection; return collection.outEdges(documentHandle, {}); } /** * collection traversal - Performs a traversal starting from the given * startVertex and following edges contained in this edge collection. * * @param {String} collectionName collection name * @param {String | String[]} startVertex Start vertex or vertices. * This can be either the _id of a document in the database, * the _key of an edge in the collection, or a document * (i.e. an object with an _id or _key property). * @param {any} opts opts.direction opts.filter, opts.visitor, * opts.init, opts.expander, opts.sort * @return {[Object]} edge traversal path */ async traversal(vertices, collection, opts, filters) { if (vertices) { if (_.isEmpty(vertices.collection_name) && !_.isEmpty(vertices.start_vertex_ids)) { throw new Error(`missing collection name for vertex id ${vertices.start_vertex_ids}`); } else if (!_.isEmpty(vertices.collection_name) && _.isEmpty(vertices.start_vertex_ids)) { throw new Error(`missing vertex id for collection_name ${vertices.collection_name}`); } } // vertices data let vertexCollectionName, startVertexIds; if (vertices) { vertexCollectionName = vertices.collection_name; startVertexIds = vertices.start_vertex_ids; } // collection data let collectionName, limit, offset, sort; if (collection) { collectionName = collection.collection_name; limit = collection.limit; offset = collection.offset; sort = collection.sorts; } if ((_.isUndefined(startVertexIds) || _.isNil(startVertexIds) || _.isEmpty(startVertexIds)) && (_.isUndefined(collectionName) || _.isNil(collectionName) || _.isEmpty(collectionName))) { throw new Error('One of the Vertices or Collection should be defined'); } // from either vertices or collections const traversalCollectionName = collectionName && !_.isEmpty(collectionName) ? collectionName : vertexCollectionName; if (!opts) { opts = {}; } // make outbound traversal by default if not provided if (opts.direction === undefined) { opts.direction = graph_1.Options_Direction.OUTBOUND; } // default options let defaultOptions = { uniqueVertices: 'global', bfs: true, uniqueEdges: 'path' }; let filter = ''; let rootFilter = ''; let limitFilter = ''; let sortFilter = ''; // include vertices in options if specified if (opts.include_vertexs) { defaultOptions.vertexCollections = opts.include_vertexs; } // include edges in options if specified if (opts.include_edges) { defaultOptions.edgeCollections = opts.include_edges; } // exclude vertices if (opts.exclude_vertexs) { for (const excludeVertex of opts.exclude_vertexs) { filter = filter + ` FILTER v._id NOT LIKE "${excludeVertex}%" `; } } // exclude edges if (opts.exclude_edges) { for (const excludeEdge of opts.exclude_edges) { filter = filter + ` FILTER e._id NOT LIKE "${excludeEdge}%" `; } } const rootAndAssociationFilter = (0, utils_1.createGraphsAssociationFilter)(filters, opts.direction, traversalCollectionName, this.edgeDefConfig, filter); // association fitler filter = rootAndAssociationFilter.associationFilter; // root filter const rootEntityFilter = rootAndAssociationFilter.rootEntityFilter; if (rootEntityFilter) { rootFilter = (0, utils_1.buildGraphFilter)([rootEntityFilter], true).q; } if (startVertexIds && startVertexIds.length > 0) { if (rootFilter && !_.isEmpty(rootFilter)) { rootFilter = ` obj.id IN ${JSON.stringify(startVertexIds)} && ${rootFilter}`; } else { rootFilter = ` obj.id IN ${JSON.stringify(startVertexIds)} `; } } // combined root filter if (rootFilter && !_.isEmpty(rootFilter)) { rootFilter = `FILTER ${rootFilter}`; } limitFilter = (0, utils_1.buildGraphLimiter)(limit, offset); if (sort) { sortFilter = (0, utils_1.buildGraphSorter)(sort); } let rootCursor, associationCursor; try { defaultOptions = JSON.stringify(defaultOptions); // traversal data const traversalQuery = `For obj IN ${traversalCollectionName} ${rootFilter} ${limitFilter} ${sortFilter} FOR v, e, p IN 1..100 ${graph_1.Options_Direction[opts.direction]} obj GRAPH "${this.graph.name}" OPTIONS ${defaultOptions} ${filter} RETURN { v, e, p }`; associationCursor = await this.db.query(traversalQuery); const rootEntityQuery = `For obj IN ${traversalCollectionName} ${rootFilter} ${limitFilter} ${sortFilter} return obj`; rootCursor = await this.db.query(rootEntityQuery); } catch (err) { throw { code: err.code, message: err.message }; } return { rootCursor, associationCursor }; } async getAllChildrenNodes(startVertex, edgeName) { const queryTpl = `FOR v IN 1..1 OUTBOUND @start_vertex @@edge_name RETURN v`; const result = await this.db.query(queryTpl, { start_vertex: startVertex, '@edge_name': edgeName }); return result; } arrUnique(arr) { return [...new Set(arr)]; } /** * Adds the given edge definition to the graph. * * @param {string} edgeName edge name * @param {Object} fromVertice from vertice * @param {Object} toVertice from vertice * @return {Object} The added edge definition */ async addEdgeDefinition(edgeName, fromVertice, toVertice) { if (_.isNil(edgeName)) { throw new Error('missing edge name'); } if (_.isNil(fromVertice)) { throw new Error('missing from vertice'); } if (_.isNil(toVertice)) { throw new Error('missing to vertice'); } if (!_.isArray(fromVertice)) { fromVertice = [fromVertice]; } if (!_.isArray(toVertice)) { toVertice = [toVertice]; } let edgeDef; try { edgeDef = await this.graph.addEdgeDefinition({ collection: edgeName, from: fromVertice, to: toVertice }); } catch (err) { // if edge def already exists return if (err.message === `${edgeName} multi use of edge collection in edge def`) { return edgeDef; } throw { code: err.code, message: err.message }; } } /** * Removes the edge definition with the given definitionName form the graph. * * @param {string} definitionName Name of the edge definition * to remove from the graph. * @param {boolean} dropCollection If set to true, the edge collection * associated with the definition will also be deleted from the database. * @return {Object} replaced edge definition */ async removeEdgeDefinition(definitionName, dropCollection) { if (_.isNil(definitionName)) { throw new Error('missing definition name'); } return this.graph.removeEdgeDefinition(definitionName, dropCollection); } /** * list graphs. * * @return {Promise<any>} list all the graphs */ async listGraphs() { return this.db.listGraphs(); } /** * Deletes the edge with the given documentHandle from the collection. * * @param {string} collectionName edge collection name * @param {string} documentHandle The handle of the edge to retrieve. * This can be either the _id or the _key of an edge in the collection, * or an edge (i.e. an object with an _id or _key property). * @return {Object} removed Edge */ async removeEdge(collectionName, documentHandle) { if (_.isNil(collectionName)) { throw new Error('missing edge collection name'); } if (_.isNil(documentHandle)) { throw new Error('missing document handle'); } const collection = this.graph.edgeCollection(collectionName); return collection.remove(documentHandle); } } exports.ArangoGraph = ArangoGraph; //# sourceMappingURL=graph.js.map