import { NeighborVertexBasicInfo } from "./GraphExplorer"; import * as GraphData from "./GraphData"; import * as ViewModels from "../../../Contracts/ViewModels"; interface JoinArrayMaxCharOutput { result: string; // string output consumedCount: number; // Number of items consumed } export class GraphUtil { public static getNeighborTitle(neighbor: NeighborVertexBasicInfo): string { return `edge id: ${neighbor.edgeId}, vertex id: ${neighbor.id}`; } /** * Collect all edges from this node * @param vertex * @param graphData * @param newNodes (optional) object describing new nodes encountered */ public static createEdgesfromNode( vertex: GraphData.GremlinVertex, graphData: GraphData.GraphData, newNodes?: { [id: string]: boolean } ): void { if (vertex.hasOwnProperty("outE")) { let outE = vertex.outE; for (var label in outE) { $.each(outE[label], (index: number, edge: any) => { // We create our own edge. No need to fetch let e = { id: edge.id, label: label, inV: edge.inV, outV: vertex.id }; graphData.addEdge(e); if (newNodes) { newNodes[edge.inV] = true; } }); } } if (vertex.hasOwnProperty("inE")) { let inE = vertex.inE; for (var label in inE) { $.each(inE[label], (index: number, edge: any) => { // We create our own edge. No need to fetch let e = { id: edge.id, label: label, inV: vertex.id, outV: edge.outV }; graphData.addEdge(e); if (newNodes) { newNodes[edge.outV] = true; } }); } } } /** * From ['id1', 'id2', 'idn'] build the following string "'id1','id2','idn'". * The string length cannot exceed maxSize. * @param array * @param maxSize * @return */ public static getLimitedArrayString(array: string[], maxSize: number): JoinArrayMaxCharOutput { if (!array || array.length === 0 || array[0].length + 2 > maxSize) { return { result: "", consumedCount: 0 }; } const end = array.length - 1; let output = `'${array[0]}'`; let i = 0; for (; i < end; i++) { const candidate = `${output},'${array[i + 1]}'`; if (candidate.length <= maxSize) { output = candidate; } else { break; } } return { result: output, consumedCount: i + 1 }; } public static createFetchEdgePairQuery( outE: boolean, pkid: string, excludedEdgeIds: string[], startIndex: number, pageSize: number, withoutStepArgMaxLenght: number ): string { let gremlinQuery: string; if (excludedEdgeIds.length > 0) { // build a string up to max char const joined = GraphUtil.getLimitedArrayString(excludedEdgeIds, withoutStepArgMaxLenght); const hasWithoutStep = !!joined.result ? `.has(id, without(${joined.result}))` : ""; if (joined.consumedCount === excludedEdgeIds.length) { gremlinQuery = `g.V(${pkid}).${outE ? "outE" : "inE"}()${hasWithoutStep}.limit(${pageSize}).as('e').${ outE ? "inV" : "outV" }().as('v').select('e', 'v')`; } else { const start = startIndex - joined.consumedCount; gremlinQuery = `g.V(${pkid}).${outE ? "outE" : "inE"}()${hasWithoutStep}.range(${start},${start + pageSize}).as('e').${outE ? "inV" : "outV"}().as('v').select('e', 'v')`; } } else { gremlinQuery = `g.V(${pkid}).${outE ? "outE" : "inE"}().limit(${pageSize}).as('e').${ outE ? "inV" : "outV" }().as('v').select('e', 'v')`; } return gremlinQuery; } /** * Trim graph */ public static trimGraph( currentRoot: GraphData.GremlinVertex, graphData: GraphData.GraphData ) { const importantNodes = [currentRoot.id].concat(currentRoot._ancestorsId); graphData.unloadAllVertices(importantNodes); // Keep only ancestors node in fixed position $.each(graphData.ids, (index: number, id: string) => { graphData.getVertexById(id)._isFixedPosition = importantNodes.indexOf(id) !== -1; }); } public static addRootChildToGraph( root: GraphData.GremlinVertex, child: GraphData.GremlinVertex, graphData: GraphData.GraphData ) { child._ancestorsId = (root._ancestorsId || []).concat([root.id]); graphData.addVertex(child); GraphUtil.createEdgesfromNode(child, graphData); graphData.addNeighborInfo(child); } /** * TODO Perform minimal substitution to prevent breaking gremlin query and allow \"" for now. * @param value */ public static escapeDoubleQuotes(value: string): string { return value == null ? value : value.replace(/"/g, '\\"'); } /** * Surround with double-quotes if val is a string. * @param val */ public static getQuotedPropValue(ip: ViewModels.InputPropertyValue): string { switch (ip.type) { case "number": case "boolean": return `${ip.value}`; case "null": return null; default: return `"${GraphUtil.escapeDoubleQuotes(ip.value as string)}"`; } } /** * TODO Perform minimal substitution to prevent breaking gremlin query and allow \' for now. * @param value */ public static escapeSingleQuotes(value: string): string { return value == null ? value : value.replace(/'/g, "\\'"); } }