@lowgular/code-graph 0.1.1 → 0.1.2
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/bin/cli.js +2458 -5
- package/lib.js +2430 -0
- package/package.json +3 -2
- package/code-graph-v2/code.graph.js +0 -37
- package/code-graph-v2/config-from-query.js +0 -131
- package/code-graph-v2/extractors/extractor.js +0 -27
- package/code-graph-v2/extractors/index.js +0 -1
- package/code-graph-v2/graph-builder/code-graph.builder.js +0 -49
- package/code-graph-v2/graph-builder/index.js +0 -1
- package/code-graph-v2/graph-builder/node.processor.js +0 -22
- package/code-graph-v2/graph-builder/relationship.processor.js +0 -55
- package/code-graph-v2/graph-builder/type.processor.js +0 -21
- package/code-graph-v2/index.js +0 -4
- package/code-graph-v2/tools/build-code-graph.tool.js +0 -19
- package/code-graph-v2/utils.js +0 -34
- package/codegular/index.js +0 -5
- package/codegular/node.js +0 -71
- package/codegular/program.js +0 -100
- package/codegular/string.js +0 -121
- package/codegular/type-checker.js +0 -133
- package/codegular/type.js +0 -356
- package/codegular/utils.js +0 -335
- package/cypher/index.js +0 -1
- package/cypher/lib/executor/condition-evaluator.js +0 -135
- package/cypher/lib/executor/executor.js +0 -60
- package/cypher/lib/executor/graph.js +0 -0
- package/cypher/lib/executor/match-engine.js +0 -130
- package/cypher/lib/executor/pattern-matcher.js +0 -86
- package/cypher/lib/executor/relationship-navigator.js +0 -41
- package/cypher/lib/executor/result-formatter.js +0 -149
- package/cypher/lib/executor/traverse-engine.js +0 -141
- package/cypher/lib/executor/utils.js +0 -14
- package/cypher/lib/graph.stub.js +0 -38
- package/cypher/lib/index.js +0 -32
- package/cypher/lib/lexer.js +0 -376
- package/cypher/lib/parser.js +0 -586
- package/cypher/lib/validator/query-validator.js +0 -75
- package/cypher/lib/validator/supported-features.config.js +0 -83
- package/cypher/lib/validator/unsupported-features.config.js +0 -124
- package/cypher-cli.js +0 -41
- package/infra/code-graph.js +0 -147
- package/main.js +0 -0
- package/resources-cli.js +0 -75
- package/run-cli.js +0 -43
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
class PatternMatcher {
|
|
2
|
-
/**
|
|
3
|
-
* Check if an edge type matches the relationship pattern's edge type(s)
|
|
4
|
-
* Supports both single edge type (string) and multiple edge types (array)
|
|
5
|
-
*/
|
|
6
|
-
matchesEdgeType(edgeType, patternEdgeType) {
|
|
7
|
-
if (!patternEdgeType) {
|
|
8
|
-
return true;
|
|
9
|
-
}
|
|
10
|
-
if (typeof patternEdgeType === "string") {
|
|
11
|
-
return edgeType === patternEdgeType;
|
|
12
|
-
}
|
|
13
|
-
return patternEdgeType.includes(edgeType);
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Deep equality check for arrays
|
|
17
|
-
*/
|
|
18
|
-
arraysEqual(a, b) {
|
|
19
|
-
if (a.length !== b.length) {
|
|
20
|
-
return false;
|
|
21
|
-
}
|
|
22
|
-
for (let i = 0; i < a.length; i++) {
|
|
23
|
-
if (Array.isArray(a[i]) && Array.isArray(b[i])) {
|
|
24
|
-
if (!this.arraysEqual(a[i], b[i])) {
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
} else if (a[i] !== b[i]) {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return true;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Check if two values are equal (handles arrays with deep equality)
|
|
35
|
-
*/
|
|
36
|
-
valuesEqual(expected, actual) {
|
|
37
|
-
if (Array.isArray(expected) && Array.isArray(actual)) {
|
|
38
|
-
return this.arraysEqual(expected, actual);
|
|
39
|
-
}
|
|
40
|
-
return expected === actual;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Check if a node matches a pattern (labels and properties)
|
|
44
|
-
*/
|
|
45
|
-
doesNodeMatchPattern(node, pattern) {
|
|
46
|
-
if (pattern.labels.length > 0) {
|
|
47
|
-
const labelOperator = pattern.labelOperator || "AND";
|
|
48
|
-
if (labelOperator === "AND") {
|
|
49
|
-
if (!pattern.labels.every((label) => node.labels.includes(label))) {
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
} else {
|
|
53
|
-
if (!pattern.labels.some((label) => node.labels.includes(label))) {
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
if (pattern.properties) {
|
|
59
|
-
const notFound = Object.entries(pattern.properties).find(
|
|
60
|
-
([key, expectedValue]) => !this.valuesEqual(expectedValue, node[key])
|
|
61
|
-
) !== void 0;
|
|
62
|
-
if (notFound) {
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return true;
|
|
67
|
-
}
|
|
68
|
-
findMatchingNodes(graph, pattern) {
|
|
69
|
-
return Object.values(graph.nodesById).find(
|
|
70
|
-
(node) => this.doesNodeMatchPattern(node, pattern)
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Match a single node pattern
|
|
75
|
-
* Finds all nodes in the graph that match the given pattern
|
|
76
|
-
* Reuses matchesPattern to avoid code duplication
|
|
77
|
-
*/
|
|
78
|
-
filterMatchingNodes(graph, pattern) {
|
|
79
|
-
return Object.values(graph.nodesById).filter(
|
|
80
|
-
(node) => this.doesNodeMatchPattern(node, pattern)
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
export {
|
|
85
|
-
PatternMatcher
|
|
86
|
-
};
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
class RelationshipNavigator {
|
|
2
|
-
constructor(patternMatcher) {
|
|
3
|
-
this.patternMatcher = patternMatcher;
|
|
4
|
-
}
|
|
5
|
-
/**
|
|
6
|
-
* Find nodes and edges connected via a relationship from the given node
|
|
7
|
-
* Returns both the connected node and the edge that connects them
|
|
8
|
-
*/
|
|
9
|
-
findConnectedNodesAndEdges(graph, node, relationship) {
|
|
10
|
-
const connections = [];
|
|
11
|
-
const seenNodeIds = /* @__PURE__ */ new Set();
|
|
12
|
-
if (relationship.direction === "outgoing" || relationship.direction === "both") {
|
|
13
|
-
const outgoingEdges = graph.edgesBySource[node.id] || [];
|
|
14
|
-
for (const edge of outgoingEdges) {
|
|
15
|
-
if (this.patternMatcher.matchesEdgeType(edge.type, relationship.edgeType)) {
|
|
16
|
-
const targetNode = graph.nodesById[edge.target];
|
|
17
|
-
if (targetNode && !seenNodeIds.has(targetNode.id)) {
|
|
18
|
-
seenNodeIds.add(targetNode.id);
|
|
19
|
-
connections.push({ node: targetNode, edge });
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
if (relationship.direction === "incoming" || relationship.direction === "both") {
|
|
25
|
-
const incomingEdges = graph.edgesByTarget[node.id] || [];
|
|
26
|
-
for (const edge of incomingEdges) {
|
|
27
|
-
if (this.patternMatcher.matchesEdgeType(edge.type, relationship.edgeType)) {
|
|
28
|
-
const sourceNode = graph.nodesById[edge.source];
|
|
29
|
-
if (sourceNode && !seenNodeIds.has(sourceNode.id)) {
|
|
30
|
-
seenNodeIds.add(sourceNode.id);
|
|
31
|
-
connections.push({ node: sourceNode, edge });
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return connections;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
export {
|
|
40
|
-
RelationshipNavigator
|
|
41
|
-
};
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import { Annonymizer } from "./utils.js";
|
|
2
|
-
const extractGraphNodesFromResult = (results) => {
|
|
3
|
-
return Object.values(results).filter(
|
|
4
|
-
(value) => value !== null && typeof value === "object" && "id" in value && "labels" in value
|
|
5
|
-
);
|
|
6
|
-
};
|
|
7
|
-
class ResultFormatter {
|
|
8
|
-
constructor() {
|
|
9
|
-
this.annonymizer = new Annonymizer();
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Format a single match value (node or edge) based on extractData flag
|
|
13
|
-
*/
|
|
14
|
-
formatValue(value) {
|
|
15
|
-
if (value === null || value === void 0) {
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
18
|
-
if ("id" in value && "labels" in value) {
|
|
19
|
-
const node = value;
|
|
20
|
-
return node;
|
|
21
|
-
} else {
|
|
22
|
-
const edge = value;
|
|
23
|
-
return edge;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Sort results based on ORDER BY clause
|
|
28
|
-
*/
|
|
29
|
-
sortResults(results, orderBy) {
|
|
30
|
-
return [...results].sort((a, b) => {
|
|
31
|
-
for (const item of orderBy.items) {
|
|
32
|
-
const valA = this.getPropertyValue(a[item.variable], item.property);
|
|
33
|
-
const valB = this.getPropertyValue(b[item.variable], item.property);
|
|
34
|
-
const comparison = this.compareValues(valA, valB, item.direction);
|
|
35
|
-
if (comparison !== 0)
|
|
36
|
-
return comparison;
|
|
37
|
-
}
|
|
38
|
-
return 0;
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Get property value from an object, handling nested property access
|
|
43
|
-
*/
|
|
44
|
-
getPropertyValue(obj, property) {
|
|
45
|
-
if (obj === null || obj === void 0)
|
|
46
|
-
return null;
|
|
47
|
-
return property ? obj[property] : obj;
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Compare two values with Neo4j-style NULL handling
|
|
51
|
-
* ASC: NULLs first, then ascending values
|
|
52
|
-
* DESC: Values descending, then NULLs last
|
|
53
|
-
*/
|
|
54
|
-
compareValues(a, b, direction) {
|
|
55
|
-
const aIsNull = a === null || a === void 0;
|
|
56
|
-
const bIsNull = b === null || b === void 0;
|
|
57
|
-
if (aIsNull && bIsNull)
|
|
58
|
-
return 0;
|
|
59
|
-
if (aIsNull)
|
|
60
|
-
return direction === "ASC" ? -1 : 1;
|
|
61
|
-
if (bIsNull)
|
|
62
|
-
return direction === "ASC" ? 1 : -1;
|
|
63
|
-
let result;
|
|
64
|
-
if (typeof a === "string" && typeof b === "string") {
|
|
65
|
-
result = a.localeCompare(b);
|
|
66
|
-
} else {
|
|
67
|
-
result = a < b ? -1 : a > b ? 1 : 0;
|
|
68
|
-
}
|
|
69
|
-
return direction === "DESC" ? -result : result;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Format results with RETURN clause
|
|
73
|
-
* Only includes variables specified in RETURN clause, with optional aliases
|
|
74
|
-
* If returnAll is true (RETURN *), returns all variables
|
|
75
|
-
* If distinct is true, deduplicate results based on returned variables' ids
|
|
76
|
-
*/
|
|
77
|
-
formatResults(results, returnClause) {
|
|
78
|
-
let processedResults = returnClause.limit ? results.slice(0, returnClause.limit) : results;
|
|
79
|
-
if (returnClause.distinct) {
|
|
80
|
-
processedResults = this.deduplicateResults(processedResults, returnClause);
|
|
81
|
-
}
|
|
82
|
-
if (returnClause.returnAll) {
|
|
83
|
-
return this.formatDefault(processedResults);
|
|
84
|
-
}
|
|
85
|
-
return processedResults.map((match) => {
|
|
86
|
-
const result = {};
|
|
87
|
-
for (const item of returnClause.items) {
|
|
88
|
-
const variable = item.expression;
|
|
89
|
-
if (this.annonymizer.isAnonymous(variable)) {
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
const alias = item.alias || variable;
|
|
93
|
-
if (variable in match) {
|
|
94
|
-
const value = match[variable];
|
|
95
|
-
result[alias] = this.formatValue(value);
|
|
96
|
-
} else {
|
|
97
|
-
throw new Error(`Variable ${variable} not found in match results`);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
return result;
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Deduplicate results based on returned variables' ids
|
|
105
|
-
*/
|
|
106
|
-
deduplicateResults(results, returnClause) {
|
|
107
|
-
const seen = /* @__PURE__ */ new Map();
|
|
108
|
-
for (const match of results) {
|
|
109
|
-
const key = returnClause.items.map((item) => this.getValueId(match[item.expression])).join("|");
|
|
110
|
-
if (!seen.has(key)) {
|
|
111
|
-
seen.set(key, match);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
return Array.from(seen.values());
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Get a unique identifier for a match result value
|
|
118
|
-
*/
|
|
119
|
-
getValueId(value) {
|
|
120
|
-
if (value === null || value === void 0) {
|
|
121
|
-
return "null";
|
|
122
|
-
}
|
|
123
|
-
if ("id" in value && "labels" in value) {
|
|
124
|
-
return value.id;
|
|
125
|
-
}
|
|
126
|
-
const edge = value;
|
|
127
|
-
return `edge:${edge.source}|${edge.target}|${edge.type}`;
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Format results without RETURN clause (default)
|
|
131
|
-
* Returns all matched variables keyed by their variable names
|
|
132
|
-
*/
|
|
133
|
-
formatDefault(matches) {
|
|
134
|
-
return matches.map((match) => {
|
|
135
|
-
const result = {};
|
|
136
|
-
for (const [varName, value] of Object.entries(match)) {
|
|
137
|
-
if (this.annonymizer.isAnonymous(varName)) {
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
result[varName] = this.formatValue(value);
|
|
141
|
-
}
|
|
142
|
-
return result;
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
export {
|
|
147
|
-
ResultFormatter,
|
|
148
|
-
extractGraphNodesFromResult
|
|
149
|
-
};
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { Annonymizer } from "./utils.js";
|
|
2
|
-
class TraverseEngine {
|
|
3
|
-
constructor(patternMatcher, relationshipNavigator) {
|
|
4
|
-
this.patternMatcher = patternMatcher;
|
|
5
|
-
this.relationshipNavigator = relationshipNavigator;
|
|
6
|
-
this.annonymizer = new Annonymizer();
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Recursively traverse relationships to match subsequent patterns
|
|
10
|
-
* Supports returning edges as variables when relationship variable is specified
|
|
11
|
-
*/
|
|
12
|
-
traverseRelationships(graph, currentNode, currentVarName, remainingPatterns, relationships) {
|
|
13
|
-
if (remainingPatterns.length === 0) {
|
|
14
|
-
return [{ [currentVarName]: currentNode }];
|
|
15
|
-
}
|
|
16
|
-
const results = [];
|
|
17
|
-
const nextPattern = remainingPatterns[0];
|
|
18
|
-
const nextRelationship = relationships[0];
|
|
19
|
-
const nextVarName = nextPattern.variable || this.annonymizer.generate(currentNode);
|
|
20
|
-
const remainingPatterns_ = remainingPatterns.slice(1);
|
|
21
|
-
const remainingRelationships = relationships.slice(1);
|
|
22
|
-
if (nextRelationship.variableLength) {
|
|
23
|
-
return this.traverseVariableLengthPath(
|
|
24
|
-
graph,
|
|
25
|
-
currentNode,
|
|
26
|
-
currentVarName,
|
|
27
|
-
nextRelationship,
|
|
28
|
-
nextPattern,
|
|
29
|
-
nextVarName,
|
|
30
|
-
remainingPatterns_,
|
|
31
|
-
remainingRelationships
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
const connections = this.relationshipNavigator.findConnectedNodesAndEdges(
|
|
35
|
-
graph,
|
|
36
|
-
currentNode,
|
|
37
|
-
nextRelationship
|
|
38
|
-
);
|
|
39
|
-
for (const { node: connectedNode, edge } of connections) {
|
|
40
|
-
if (this.patternMatcher.doesNodeMatchPattern(connectedNode, nextPattern)) {
|
|
41
|
-
const subMatches = this.traverseRelationships(
|
|
42
|
-
graph,
|
|
43
|
-
connectedNode,
|
|
44
|
-
nextVarName,
|
|
45
|
-
remainingPatterns_,
|
|
46
|
-
remainingRelationships
|
|
47
|
-
);
|
|
48
|
-
for (const subMatch of subMatches) {
|
|
49
|
-
const result = {
|
|
50
|
-
[currentVarName]: currentNode,
|
|
51
|
-
[nextVarName]: connectedNode,
|
|
52
|
-
// Always include the connected node
|
|
53
|
-
...subMatch
|
|
54
|
-
};
|
|
55
|
-
if (nextRelationship.variable && edge) {
|
|
56
|
-
result[nextRelationship.variable] = edge;
|
|
57
|
-
}
|
|
58
|
-
results.push(result);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return results;
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Traverse variable-length paths (zero or more edges)
|
|
66
|
-
* Handles cycles by tracking visited nodes
|
|
67
|
-
*/
|
|
68
|
-
traverseVariableLengthPath(graph, startNode, startVarName, relationship, targetPattern, targetVarName, remainingPatterns, remainingRelationships) {
|
|
69
|
-
const results = [];
|
|
70
|
-
const minLength = relationship.minLength ?? 0;
|
|
71
|
-
const maxLength = relationship.maxLength ?? Infinity;
|
|
72
|
-
const traverse = (currentNode, pathLength, visited, pathEdges) => {
|
|
73
|
-
if (pathLength >= minLength && this.patternMatcher.doesNodeMatchPattern(currentNode, targetPattern)) {
|
|
74
|
-
const subMatches = this.traverseRelationships(
|
|
75
|
-
graph,
|
|
76
|
-
currentNode,
|
|
77
|
-
targetVarName,
|
|
78
|
-
remainingPatterns,
|
|
79
|
-
remainingRelationships
|
|
80
|
-
);
|
|
81
|
-
for (const subMatch of subMatches) {
|
|
82
|
-
const result = {
|
|
83
|
-
[startVarName]: startNode,
|
|
84
|
-
[targetVarName]: currentNode,
|
|
85
|
-
...subMatch
|
|
86
|
-
};
|
|
87
|
-
if (relationship.variable && pathEdges.length > 0) {
|
|
88
|
-
result[relationship.variable] = pathEdges[pathEdges.length - 1];
|
|
89
|
-
}
|
|
90
|
-
results.push(result);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
if (pathLength < maxLength) {
|
|
94
|
-
const connections2 = this.relationshipNavigator.findConnectedNodesAndEdges(
|
|
95
|
-
graph,
|
|
96
|
-
currentNode,
|
|
97
|
-
relationship
|
|
98
|
-
);
|
|
99
|
-
for (const { node: nextNode, edge } of connections2) {
|
|
100
|
-
if (!visited.has(nextNode.id)) {
|
|
101
|
-
const newVisited = new Set(visited);
|
|
102
|
-
newVisited.add(nextNode.id);
|
|
103
|
-
traverse(nextNode, pathLength + 1, newVisited, [
|
|
104
|
-
...pathEdges,
|
|
105
|
-
edge
|
|
106
|
-
]);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
if (minLength === 0 && this.patternMatcher.doesNodeMatchPattern(startNode, targetPattern)) {
|
|
112
|
-
const subMatches = this.traverseRelationships(
|
|
113
|
-
graph,
|
|
114
|
-
startNode,
|
|
115
|
-
targetVarName,
|
|
116
|
-
remainingPatterns,
|
|
117
|
-
remainingRelationships
|
|
118
|
-
);
|
|
119
|
-
for (const subMatch of subMatches) {
|
|
120
|
-
const result = {
|
|
121
|
-
[startVarName]: startNode,
|
|
122
|
-
[targetVarName]: startNode,
|
|
123
|
-
...subMatch
|
|
124
|
-
};
|
|
125
|
-
results.push(result);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
const connections = this.relationshipNavigator.findConnectedNodesAndEdges(
|
|
129
|
-
graph,
|
|
130
|
-
startNode,
|
|
131
|
-
relationship
|
|
132
|
-
);
|
|
133
|
-
for (const { node: nextNode, edge } of connections) {
|
|
134
|
-
traverse(nextNode, 1, /* @__PURE__ */ new Set([startNode.id, nextNode.id]), [edge]);
|
|
135
|
-
}
|
|
136
|
-
return results;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
export {
|
|
140
|
-
TraverseEngine
|
|
141
|
-
};
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
class Annonymizer {
|
|
2
|
-
constructor() {
|
|
3
|
-
this.anonymousVariablePrefix = "annonymous_";
|
|
4
|
-
}
|
|
5
|
-
generate(node) {
|
|
6
|
-
return this.anonymousVariablePrefix + node.id;
|
|
7
|
-
}
|
|
8
|
-
isAnonymous(variable) {
|
|
9
|
-
return variable.startsWith(this.anonymousVariablePrefix);
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
export {
|
|
13
|
-
Annonymizer
|
|
14
|
-
};
|
package/cypher/lib/graph.stub.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
class GraphBuilder {
|
|
2
|
-
constructor() {
|
|
3
|
-
this.graph = {
|
|
4
|
-
nodesById: {},
|
|
5
|
-
edgesBySource: {},
|
|
6
|
-
edgesByTarget: {}
|
|
7
|
-
};
|
|
8
|
-
}
|
|
9
|
-
addNode(node) {
|
|
10
|
-
this.graph.nodesById[node.id] = node;
|
|
11
|
-
return this;
|
|
12
|
-
}
|
|
13
|
-
addEdge(edge) {
|
|
14
|
-
if (!this.graph.edgesBySource[edge.source]) {
|
|
15
|
-
this.graph.edgesBySource[edge.source] = [];
|
|
16
|
-
}
|
|
17
|
-
if (!this.graph.edgesByTarget[edge.target]) {
|
|
18
|
-
this.graph.edgesByTarget[edge.target] = [];
|
|
19
|
-
}
|
|
20
|
-
this.graph.edgesBySource[edge.source].push(edge);
|
|
21
|
-
this.graph.edgesByTarget[edge.target].push(edge);
|
|
22
|
-
return this;
|
|
23
|
-
}
|
|
24
|
-
build() {
|
|
25
|
-
return this.graph;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
const stubNode = (id, label = "A", extras = {}) => ({ id, labels: Array.isArray(label) ? label : [label], ...extras });
|
|
29
|
-
const stubEdge = (source, target, type = "A") => ({
|
|
30
|
-
source,
|
|
31
|
-
target,
|
|
32
|
-
type
|
|
33
|
-
});
|
|
34
|
-
export {
|
|
35
|
-
GraphBuilder,
|
|
36
|
-
stubEdge,
|
|
37
|
-
stubNode
|
|
38
|
-
};
|
package/cypher/lib/index.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { CypherExecutor } from "./executor/executor.js";
|
|
2
|
-
import { Lexer } from "./lexer.js";
|
|
3
|
-
import { Parser } from "./parser.js";
|
|
4
|
-
import { validateQuery } from "./validator/query-validator.js";
|
|
5
|
-
const buildQueryObject = (query2) => {
|
|
6
|
-
const joinedQuery = Array.isArray(query2) ? query2.join(" ") : query2;
|
|
7
|
-
validateQuery(joinedQuery);
|
|
8
|
-
const lexer = new Lexer(joinedQuery);
|
|
9
|
-
const tokens = lexer.tokenize();
|
|
10
|
-
const parser = new Parser(tokens);
|
|
11
|
-
return parser.parse();
|
|
12
|
-
};
|
|
13
|
-
const query = (queryStatements, graph) => {
|
|
14
|
-
const queryObject = buildQueryObject(queryStatements);
|
|
15
|
-
const executor = new CypherExecutor(graph);
|
|
16
|
-
return executor.execute(queryObject);
|
|
17
|
-
};
|
|
18
|
-
const matchAll = (query2, graph) => {
|
|
19
|
-
const queryObject = buildQueryObject(query2);
|
|
20
|
-
queryObject.statements.push({
|
|
21
|
-
type: "ReturnStatement",
|
|
22
|
-
returnAll: true,
|
|
23
|
-
items: []
|
|
24
|
-
});
|
|
25
|
-
const executor = new CypherExecutor(graph);
|
|
26
|
-
return executor.execute(queryObject);
|
|
27
|
-
};
|
|
28
|
-
export {
|
|
29
|
-
buildQueryObject,
|
|
30
|
-
matchAll,
|
|
31
|
-
query
|
|
32
|
-
};
|