gcyphrq 0.12.6
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/LICENSE +21 -0
- package/README.md +179 -0
- package/dist/engine/cypher-engine.d.ts +39 -0
- package/dist/engine/cypher-engine.d.ts.map +1 -0
- package/dist/engine/cypher-parser.d.ts +3 -0
- package/dist/engine/cypher-parser.d.ts.map +1 -0
- package/dist/graph.d.ts +24 -0
- package/dist/graph.d.ts.map +1 -0
- package/dist/index.js +5816 -0
- package/dist/indexes.d.ts +38 -0
- package/dist/indexes.d.ts.map +1 -0
- package/dist/lib.d.ts +189 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +1455 -0
- package/dist/types/cypher.d.ts +139 -0
- package/dist/types/cypher.d.ts.map +1 -0
- package/docs/404.md +11 -0
- package/docs/_config.yml +42 -0
- package/docs/_includes/footer.html +6 -0
- package/docs/_includes/head.html +13 -0
- package/docs/_includes/nav.html +23 -0
- package/docs/_layouts/default.html +20 -0
- package/docs/assets/cypher-highlight.js +171 -0
- package/docs/assets/favicon.svg +4 -0
- package/docs/assets/main.css +416 -0
- package/docs/cli.md +115 -0
- package/docs/examples.md +223 -0
- package/docs/getting-started.md +158 -0
- package/docs/index.md +89 -0
- package/docs/library-api.md +618 -0
- package/docs/query-guide.md +302 -0
- package/docs/skill.md +219 -0
- package/examples/README.md +187 -0
- package/examples/cloud-infra.json +199 -0
- package/examples/social-graph.json +11 -0
- package/package.json +71 -0
|
@@ -0,0 +1,618 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Library API
|
|
4
|
+
description: Complete API reference for using gcyphrq as a Node.js / TypeScript library.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<div class="breadcrumb">
|
|
8
|
+
<a href="{{ '/' | relative_url }}">Home</a> <span>›</span> Library API
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
# Library API
|
|
12
|
+
|
|
13
|
+
`gcyphrq` can be used as a library in Node.js and TypeScript projects. This page covers all public APIs, types, and usage patterns.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install gcyphrq
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { executeQuery } from 'gcyphrq';
|
|
25
|
+
|
|
26
|
+
const graphData = {
|
|
27
|
+
nodes: [
|
|
28
|
+
{ id: 'alice', label: 'User', name: 'Alice', age: 30 },
|
|
29
|
+
{ id: 'bob', label: 'User', name: 'Bob', age: 25 },
|
|
30
|
+
],
|
|
31
|
+
edges: [
|
|
32
|
+
{ source: 'alice', target: 'bob', type: 'FRIEND' },
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const results = executeQuery(graphData, 'MATCH (u:User) RETURN u.name, u.age');
|
|
37
|
+
console.log(results);
|
|
38
|
+
// [ { name: 'Alice', age: 30 }, { name: 'Bob', age: 25 } ]
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### With an existing Graphology graph
|
|
42
|
+
|
|
43
|
+
If you already have a Graphology `Graph` instance (built externally or programmatically), you can pass it directly:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { executeQuery } from 'gcyphrq';
|
|
47
|
+
import Graph from 'graphology';
|
|
48
|
+
|
|
49
|
+
const graph = new Graph();
|
|
50
|
+
graph.addNode('alice', { label: 'User', name: 'Alice', age: 30 });
|
|
51
|
+
graph.addNode('bob', { label: 'User', name: 'Bob', age: 25 });
|
|
52
|
+
graph.addEdge('alice', 'bob', { type: 'FRIEND' });
|
|
53
|
+
|
|
54
|
+
const results = executeQuery(graph, 'MATCH (u:User) RETURN u.name, u.age');
|
|
55
|
+
console.log(results);
|
|
56
|
+
// [ { name: 'Alice', age: 30 }, { name: 'Bob', age: 25 } ]
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
No graph data reconstruction needed — the library builds indexes directly from the graph instance.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## API Reference
|
|
64
|
+
|
|
65
|
+
### `executeQuery(graphData, query)`
|
|
66
|
+
|
|
67
|
+
Execute a Cypher query against a graph and return results as plain JSON. This is the simplest entry point — pass in graph data and a query string, get back an array of result rows.
|
|
68
|
+
|
|
69
|
+
**Parameters:**
|
|
70
|
+
|
|
71
|
+
| Parameter | Type | Description |
|
|
72
|
+
|---|---|---|
|
|
73
|
+
| `graphData` | [`GraphFile`](#graphfile) | Graph data in the Graphology JSON format |
|
|
74
|
+
| `query` | `string` | A Cypher query string |
|
|
75
|
+
|
|
76
|
+
**Returns:** `ResultRow[]` — array of result rows
|
|
77
|
+
|
|
78
|
+
**Throws:** `GraphError` if graph data is invalid, `Error` if the query is invalid
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { executeQuery } from 'gcyphrq';
|
|
82
|
+
|
|
83
|
+
const results = executeQuery(graphData, 'MATCH (u:User) RETURN u.name');
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
#### `executeQuery(graph, query)` — with a Graphology Graph
|
|
87
|
+
|
|
88
|
+
Execute a Cypher query against an existing Graphology `Graph` instance. Indexes are built automatically from the graph.
|
|
89
|
+
|
|
90
|
+
**Parameters:**
|
|
91
|
+
|
|
92
|
+
| Parameter | Type | Description |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| `graph` | Any Graphology `Graph` | An existing graph instance (from `graphology` or the library's `Graph` wrapper) |
|
|
95
|
+
| `query` | `string` | A Cypher query string |
|
|
96
|
+
|
|
97
|
+
**Returns:** `ResultRow[]` — array of result rows
|
|
98
|
+
|
|
99
|
+
**Throws:** `Error` if the query is invalid
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import { executeQuery } from 'gcyphrq';
|
|
103
|
+
import Graph from 'graphology';
|
|
104
|
+
|
|
105
|
+
const graph = new Graph();
|
|
106
|
+
graph.addNode('alice', { label: 'User', name: 'Alice' });
|
|
107
|
+
graph.addNode('bob', { label: 'User', name: 'Bob' });
|
|
108
|
+
graph.addEdge('alice', 'bob', { type: 'FRIEND' });
|
|
109
|
+
|
|
110
|
+
const results = executeQuery(graph, 'MATCH (u:User) RETURN u.name');
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
### `createGraph(graphData)`
|
|
116
|
+
|
|
117
|
+
Build a `GraphInstance` from a graph data object. Validates the data and constructs a Graphology-backed graph that the Cypher engine can query.
|
|
118
|
+
|
|
119
|
+
**Parameters:**
|
|
120
|
+
|
|
121
|
+
| Parameter | Type | Description |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| `graphData` | [`GraphFile`](#graphfile) | Graph data in the Graphology JSON format |
|
|
124
|
+
|
|
125
|
+
**Returns:** `GraphInstance`
|
|
126
|
+
|
|
127
|
+
**Throws:** `GraphError` if graph data is invalid
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
import { createGraph } from 'gcyphrq';
|
|
131
|
+
|
|
132
|
+
const graph = createGraph(graphData);
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
### `parseCypher(query)`
|
|
138
|
+
|
|
139
|
+
Parse a Cypher query string into an AST. The returned AST can be passed to `GraphEngine.execute()` or inspected programmatically.
|
|
140
|
+
|
|
141
|
+
**Parameters:**
|
|
142
|
+
|
|
143
|
+
| Parameter | Type | Description |
|
|
144
|
+
|---|---|---|
|
|
145
|
+
| `query` | `string` | A Cypher query string |
|
|
146
|
+
|
|
147
|
+
**Returns:** `AdvancedCypherAST`
|
|
148
|
+
|
|
149
|
+
**Throws:** `Error` if the query cannot be parsed
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
import { parseCypher } from 'gcyphrq';
|
|
153
|
+
|
|
154
|
+
const ast = parseCypher('MATCH (u:User) RETURN u');
|
|
155
|
+
console.log(ast.stages[0].clause.sourcePattern.label); // 'User'
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
### `buildGraphIndexes`
|
|
161
|
+
|
|
162
|
+
Build pre-computed indexes for fast query execution. Pass the returned indexes to `GraphEngine` for O(1) label, property, and edge-type lookups instead of full-graph scans.
|
|
163
|
+
|
|
164
|
+
**Three overloads:**
|
|
165
|
+
|
|
166
|
+
| Signature | Description |
|
|
167
|
+
|---|---|
|
|
168
|
+
| `buildGraphIndexes(graph)` | Build indexes from an existing Graphology graph |
|
|
169
|
+
| `buildGraphIndexes(data)` | Build indexes from graph data alone (builds graph internally) |
|
|
170
|
+
| `buildGraphIndexes(data, graph)` | Build indexes from graph data + graph instance |
|
|
171
|
+
|
|
172
|
+
**Returns:** `GraphIndexes`
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
import { buildGraphIndexes, GraphEngine } from 'gcyphrq';
|
|
176
|
+
import Graph from 'graphology';
|
|
177
|
+
|
|
178
|
+
const graph = new Graph();
|
|
179
|
+
graph.addNode('alice', { label: 'User', name: 'Alice' });
|
|
180
|
+
|
|
181
|
+
const indexes = buildGraphIndexes(graph);
|
|
182
|
+
const engine = new GraphEngine(graph, indexes);
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### `GraphEngine`
|
|
186
|
+
|
|
187
|
+
The Cypher query engine class. Accepts a `GraphInstance` and executes parsed ASTs.
|
|
188
|
+
|
|
189
|
+
**Constructor:** `new GraphEngine(graph: GraphInstance, indexes?: GraphIndexes)`
|
|
190
|
+
|
|
191
|
+
| Parameter | Type | Description |
|
|
192
|
+
|---|---|---|
|
|
193
|
+
| `graph` | `GraphInstance` | A Graphology graph instance |
|
|
194
|
+
| `indexes` | `GraphIndexes` (optional) | Pre-computed indexes for O(1) lookups. Without indexes, the engine falls back to full-graph scans |
|
|
195
|
+
|
|
196
|
+
**Methods:**
|
|
197
|
+
|
|
198
|
+
| Method | Parameters | Returns | Description |
|
|
199
|
+
|---|---|---|---|
|
|
200
|
+
| `execute(ast)` | `ast: AdvancedCypherAST` | `ResultRow[]` | Execute a parsed AST against the graph |
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
import { GraphEngine, buildGraphIndexes, parseCypher } from 'gcyphrq';
|
|
204
|
+
import Graph from 'graphology';
|
|
205
|
+
|
|
206
|
+
const graph = new Graph();
|
|
207
|
+
graph.addNode('alice', { label: 'User', name: 'Alice' });
|
|
208
|
+
|
|
209
|
+
const indexes = buildGraphIndexes(graph);
|
|
210
|
+
const engine = new GraphEngine(graph, indexes);
|
|
211
|
+
const ast = parseCypher('MATCH (u:User) RETURN u.name');
|
|
212
|
+
const results = engine.execute(ast);
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
> **Tip:** For best performance, always pass pre-computed indexes to `GraphEngine`. Without indexes, every label and property lookup triggers a full-graph scan.
|
|
216
|
+
>
|
|
217
|
+
> **Note:** Indexes are invalidated after `CREATE`/`SET`/`DELETE` mutations. Subsequent MATCH/WITH stages within the same query fall back to full-graph scans to see updated graph state.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
### `Graph`
|
|
222
|
+
|
|
223
|
+
The Graphology wrapper class. Use this when you need to build a graph programmatically (node-by-node) instead of from a data object.
|
|
224
|
+
|
|
225
|
+
**Methods:**
|
|
226
|
+
|
|
227
|
+
| Method | Parameters | Returns | Description |
|
|
228
|
+
|---|---|---|---|
|
|
229
|
+
| `addNode(id, attrs?)` | `id: string`, `attrs?: Record<string, unknown>` | `void` | Add a node with optional attributes |
|
|
230
|
+
| `addEdge(a, b, attrs?)` | `a: string`, `b: string`, `attrs?: Record<string, unknown>` | `void` | Add a directed edge |
|
|
231
|
+
| `getNodeAttributes(id)` | `id: string` | `Record<string, unknown>` | Get node attributes |
|
|
232
|
+
| `getEdgeAttributes(id)` | `id: string` | `Record<string, unknown>` | Get edge attributes |
|
|
233
|
+
| `filterNodes(fn)` | `fn: (id, attrs) => boolean` | `string[]` | Filter nodes by predicate |
|
|
234
|
+
| `forEachOutboundEdge(id, cb)` | `id: string`, `cb: callback` | `void` | Iterate outbound edges |
|
|
235
|
+
| `forEachInboundEdge(id, cb)` | `id: string`, `cb: callback` | `void` | Iterate inbound edges |
|
|
236
|
+
| `forEachEdge(id, cb)` | `id: string`, `cb: callback` | `void` | Iterate all edges (undirected) |
|
|
237
|
+
| `setNodeAttribute(id, attr, value)` | `id: string`, `attr: string`, `value: unknown` | `void` | Set a node attribute |
|
|
238
|
+
| `hasNode(id)` | `id: string` | `boolean` | Check if node exists |
|
|
239
|
+
| `dropNode(id)` | `id: string` | `void` | Remove a node and its edges |
|
|
240
|
+
| `order` | — | `number` | Number of nodes in the graph |
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
import { Graph, GraphEngine } from 'gcyphrq';
|
|
244
|
+
|
|
245
|
+
const graph = new Graph();
|
|
246
|
+
graph.addNode('alice', { label: 'User', name: 'Alice' });
|
|
247
|
+
graph.addNode('bob', { label: 'User', name: 'Bob' });
|
|
248
|
+
graph.addEdge('alice', 'bob', { type: 'FRIEND' });
|
|
249
|
+
|
|
250
|
+
const engine = new GraphEngine(graph);
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
### `GraphError`
|
|
256
|
+
|
|
257
|
+
Error class thrown when graph data validation fails.
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
import { GraphError, createGraph } from 'gcyphrq';
|
|
261
|
+
|
|
262
|
+
try {
|
|
263
|
+
createGraph({ nodes: [], edges: [] });
|
|
264
|
+
} catch (err) {
|
|
265
|
+
if (err instanceof GraphError) {
|
|
266
|
+
console.error('Graph validation failed:', err.message);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Usage Patterns
|
|
274
|
+
|
|
275
|
+
### Pattern 1: One-shot query (simplest)
|
|
276
|
+
|
|
277
|
+
Use `executeQuery` when you have graph data and just need results:
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
import { executeQuery } from 'gcyphrq';
|
|
281
|
+
|
|
282
|
+
const results = executeQuery(graphData, 'MATCH (u:User) RETURN u.name');
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Pattern 2: Reusable graph with multiple queries
|
|
286
|
+
|
|
287
|
+
Use `createGraph` + `GraphEngine` when running multiple queries against the same graph:
|
|
288
|
+
|
|
289
|
+
```ts
|
|
290
|
+
import { createGraph, GraphEngine, parseCypher } from 'gcyphrq';
|
|
291
|
+
|
|
292
|
+
const graph = createGraph(graphData);
|
|
293
|
+
const engine = new GraphEngine(graph);
|
|
294
|
+
|
|
295
|
+
const users = engine.execute(parseCypher('MATCH (u:User) RETURN u.name'));
|
|
296
|
+
const counts = engine.execute(parseCypher('MATCH (u:User) RETURN count(u)'));
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Pattern 3: Programmatic graph construction
|
|
300
|
+
|
|
301
|
+
Use `Graph` directly when building the graph from non-JSON sources (database, API, etc.):
|
|
302
|
+
|
|
303
|
+
```ts
|
|
304
|
+
import { Graph, GraphEngine, parseCypher } from 'gcyphrq';
|
|
305
|
+
|
|
306
|
+
const graph = new Graph();
|
|
307
|
+
|
|
308
|
+
// Build from a database query, API response, etc.
|
|
309
|
+
for (const user of users) {
|
|
310
|
+
graph.addNode(user.id, { label: 'User', ...user.properties });
|
|
311
|
+
}
|
|
312
|
+
for (const rel of relationships) {
|
|
313
|
+
graph.addEdge(rel.from, rel.to, { type: rel.type });
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const engine = new GraphEngine(graph);
|
|
317
|
+
const results = engine.execute(parseCypher('MATCH (u:User) RETURN u'));
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Pattern 3a: External Graphology graph
|
|
321
|
+
|
|
322
|
+
Use the library with any existing Graphology `Graph` instance — for example, one built from a database, a file, or another library. The library builds indexes directly from the graph without needing the original data.
|
|
323
|
+
|
|
324
|
+
```ts
|
|
325
|
+
import { executeQuery, buildGraphIndexes, GraphEngine, parseCypher } from 'gcyphrq';
|
|
326
|
+
import Graph from 'graphology';
|
|
327
|
+
|
|
328
|
+
// Build graph from any source (database, API, file, etc.)
|
|
329
|
+
const graph = new Graph();
|
|
330
|
+
for (const node of nodesFromDatabase) {
|
|
331
|
+
graph.addNode(node.id, { label: node.label, ...node.properties });
|
|
332
|
+
}
|
|
333
|
+
for (const edge of edgesFromDatabase) {
|
|
334
|
+
graph.addEdge(edge.source, edge.target, { type: edge.type });
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// One-shot query (indexes built automatically)
|
|
338
|
+
const results = executeQuery(graph, 'MATCH (u:User) RETURN u.name');
|
|
339
|
+
|
|
340
|
+
// Or with reusable engine and indexes for multiple queries
|
|
341
|
+
const indexes = buildGraphIndexes(graph);
|
|
342
|
+
const engine = new GraphEngine(graph, indexes);
|
|
343
|
+
const users = engine.execute(parseCypher('MATCH (u:User) RETURN u.name'));
|
|
344
|
+
const count = engine.execute(parseCypher('MATCH (u:User) RETURN count(u)'));
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Pattern 4: AST inspection
|
|
348
|
+
|
|
349
|
+
Use `parseCypher` to inspect or transform the query AST before execution:
|
|
350
|
+
|
|
351
|
+
```ts
|
|
352
|
+
import { parseCypher } from 'gcyphrq';
|
|
353
|
+
|
|
354
|
+
const ast = parseCypher('MATCH (u:User {name: "Alice"}) RETURN u');
|
|
355
|
+
console.log(ast.stages[0].clause.sourcePattern.properties);
|
|
356
|
+
// { name: 'Alice' }
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Pattern 5: Mutation followed by query
|
|
360
|
+
|
|
361
|
+
The engine supports `CREATE`, `SET`, and `DELETE` mutations within queries. Mutations modify the underlying graph in-place:
|
|
362
|
+
|
|
363
|
+
```ts
|
|
364
|
+
import { createGraph, GraphEngine, parseCypher } from 'gcyphrq';
|
|
365
|
+
|
|
366
|
+
const graph = createGraph(graphData);
|
|
367
|
+
const engine = new GraphEngine(graph);
|
|
368
|
+
|
|
369
|
+
// Create a new node
|
|
370
|
+
engine.execute(parseCypher('CREATE (n:User {name: "Charlie"}) RETURN n'));
|
|
371
|
+
|
|
372
|
+
// Update a node
|
|
373
|
+
engine.execute(parseCypher('MATCH (u:User {name: "Alice"}) SET u.age = 31 RETURN u'));
|
|
374
|
+
|
|
375
|
+
// Query after mutation
|
|
376
|
+
const results = engine.execute(parseCypher('MATCH (u:User) RETURN u.name, u.age'));
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## Types
|
|
382
|
+
|
|
383
|
+
All types are exported and can be imported for use in your own code:
|
|
384
|
+
|
|
385
|
+
```ts
|
|
386
|
+
import type {
|
|
387
|
+
// Graph data
|
|
388
|
+
GraphFile,
|
|
389
|
+
GraphFileNode,
|
|
390
|
+
GraphFileEdge,
|
|
391
|
+
|
|
392
|
+
// Graph instance
|
|
393
|
+
GraphInstance,
|
|
394
|
+
GraphIndexes,
|
|
395
|
+
|
|
396
|
+
// AST types
|
|
397
|
+
AdvancedCypherAST,
|
|
398
|
+
Stage,
|
|
399
|
+
MatchClause,
|
|
400
|
+
WithClause,
|
|
401
|
+
ReturnClause,
|
|
402
|
+
WriteClause,
|
|
403
|
+
NodePattern,
|
|
404
|
+
RelationPattern,
|
|
405
|
+
Direction,
|
|
406
|
+
|
|
407
|
+
// Expression types
|
|
408
|
+
Expression,
|
|
409
|
+
PropertyAccessExpression,
|
|
410
|
+
LiteralExpression,
|
|
411
|
+
AggregationExpression,
|
|
412
|
+
BinaryExpression,
|
|
413
|
+
Projection,
|
|
414
|
+
OrderByItem,
|
|
415
|
+
|
|
416
|
+
// Result types
|
|
417
|
+
ResultRow,
|
|
418
|
+
QueryContext,
|
|
419
|
+
CypherNode,
|
|
420
|
+
CypherEdge,
|
|
421
|
+
CypherValue,
|
|
422
|
+
CypherLiteral,
|
|
423
|
+
} from 'gcyphrq';
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### Key Types
|
|
427
|
+
|
|
428
|
+
#### `GraphFile`
|
|
429
|
+
|
|
430
|
+
```ts
|
|
431
|
+
interface GraphFile {
|
|
432
|
+
nodes: GraphFileNode[];
|
|
433
|
+
edges: GraphFileEdge[];
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
interface GraphFileNode {
|
|
437
|
+
id: string;
|
|
438
|
+
label?: string;
|
|
439
|
+
[key: string]: unknown;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
interface GraphFileEdge {
|
|
443
|
+
source: string;
|
|
444
|
+
target: string;
|
|
445
|
+
type?: string;
|
|
446
|
+
[key: string]: unknown;
|
|
447
|
+
}
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
#### `ResultRow`
|
|
451
|
+
|
|
452
|
+
```ts
|
|
453
|
+
type ResultRow = Record<string, CypherNode | CypherEdge[] | CypherLiteral | null | undefined>;
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
Each result row is a plain object mapping projection aliases to their values.
|
|
457
|
+
|
|
458
|
+
#### `AdvancedCypherAST`
|
|
459
|
+
|
|
460
|
+
```ts
|
|
461
|
+
interface AdvancedCypherAST {
|
|
462
|
+
type: 'Query';
|
|
463
|
+
stages: Stage[];
|
|
464
|
+
return: ReturnClause | undefined;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
type Stage =
|
|
468
|
+
| { type: 'MATCH'; clause: MatchClause }
|
|
469
|
+
| { type: 'WITH'; clause: WithClause }
|
|
470
|
+
| { type: 'WRITE'; clause: WriteClause };
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
## Error Handling
|
|
476
|
+
|
|
477
|
+
The library throws two kinds of errors:
|
|
478
|
+
|
|
479
|
+
- **`GraphError`** — thrown when graph data validation fails (invalid format, missing fields, duplicate IDs, etc.)
|
|
480
|
+
- **`Error`** — thrown when a Cypher query cannot be parsed or executed (syntax errors, unsupported features, etc.)
|
|
481
|
+
|
|
482
|
+
```ts
|
|
483
|
+
import { executeQuery, GraphError } from 'gcyphrq';
|
|
484
|
+
|
|
485
|
+
try {
|
|
486
|
+
const results = executeQuery(graphData, query);
|
|
487
|
+
} catch (err) {
|
|
488
|
+
if (err instanceof GraphError) {
|
|
489
|
+
// Graph data is invalid
|
|
490
|
+
console.error('Invalid graph:', err.message);
|
|
491
|
+
} else {
|
|
492
|
+
// Query error
|
|
493
|
+
console.error('Query failed:', err.message);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
---
|
|
499
|
+
|
|
500
|
+
## TypeScript Configuration
|
|
501
|
+
|
|
502
|
+
No special TypeScript configuration is needed. The package ships with declaration files (`.d.ts`) and uses standard ESM module resolution.
|
|
503
|
+
|
|
504
|
+
For projects using `"moduleResolution": "node"` (older tsconfig), add:
|
|
505
|
+
|
|
506
|
+
```json
|
|
507
|
+
{
|
|
508
|
+
"compilerOptions": {
|
|
509
|
+
"moduleResolution": "bundler"
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
## CommonJS Compatibility
|
|
515
|
+
|
|
516
|
+
The library is published as ESM only. In CommonJS projects, use dynamic `import()`:
|
|
517
|
+
|
|
518
|
+
```js
|
|
519
|
+
async function main() {
|
|
520
|
+
const { executeQuery } = await import('gcyphrq');
|
|
521
|
+
const results = executeQuery(graphData, 'MATCH (u:User) RETURN u.name');
|
|
522
|
+
console.log(results);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
main();
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
## Performance Considerations
|
|
529
|
+
|
|
530
|
+
- **`executeQuery(graphData, query)`** builds a new graph for each call. For multiple queries, use `createGraph` + `GraphEngine` instead.
|
|
531
|
+
- **`executeQuery(graph, query)`** with an existing graph instance reuses the graph but still builds indexes on each call. For multiple queries, use `buildGraphIndexes` + `GraphEngine` instead.
|
|
532
|
+
- The engine processes queries in-memory with no caching. Large graphs (>10,000 nodes) may have noticeable query times.
|
|
533
|
+
- Variable-length paths use DFS traversal. Setting `maxDepth` is recommended to avoid excessive exploration.
|
|
534
|
+
|
|
535
|
+
## Benchmark
|
|
536
|
+
|
|
537
|
+
The `bench.ts` script measures query performance with and without pre-computed indexes. Each query runs 50 iterations and reports per-run average time for both indexed and non-indexed modes, plus the speedup ratio.
|
|
538
|
+
|
|
539
|
+
### Running the benchmark
|
|
540
|
+
|
|
541
|
+
```bash
|
|
542
|
+
# Default: 5 queries against examples/cloud-infra.json
|
|
543
|
+
npx tsx bench.ts
|
|
544
|
+
|
|
545
|
+
# Different graph
|
|
546
|
+
npx tsx bench.ts -g examples/social-graph.json
|
|
547
|
+
|
|
548
|
+
# Custom queries (any number of -q args)
|
|
549
|
+
npx tsx bench.ts -q 'MATCH (s:Service) RETURN s' 'MATCH (n) RETURN count(n) AS total'
|
|
550
|
+
|
|
551
|
+
# Both together
|
|
552
|
+
npx tsx bench.ts -g examples/cloud-infra.json -q 'MATCH (s:Service {type: "RPC"}) RETURN s.name'
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
### Options
|
|
556
|
+
|
|
557
|
+
| Option | Description |
|
|
558
|
+
|---|---|
|
|
559
|
+
| `-g <file>` | Path to a JSON graph file (default: `examples/cloud-infra.json`) |
|
|
560
|
+
| `-q <query> [query ...]` | One or more Cypher queries to benchmark. If omitted, runs a default set of 5 queries |
|
|
561
|
+
|
|
562
|
+
### Output
|
|
563
|
+
|
|
564
|
+
```
|
|
565
|
+
Graph: 51 nodes, 142 edges
|
|
566
|
+
|
|
567
|
+
Query | No index | Indexed | Speedup
|
|
568
|
+
─────────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
569
|
+
MATCH (s:Service) RETURN s | 0.04ms (20 rows) | 0.01ms (20 rows) | 2.6x
|
|
570
|
+
MATCH (s:Service)-[r:DEPENDS_ON*1..2]->(d) RETURN s.name, d.... | 0.04ms (0 rows) | 0.02ms (0 rows) | 2.4x
|
|
571
|
+
MATCH (n) RETURN count(n) AS total | 0.04ms (1 rows) | 0.03ms (1 rows) | 1.3x
|
|
572
|
+
MATCH (s:Service) RETURN s ORDER BY s.name SKIP 2 LIMIT 5 | 0.03ms (5 rows) | 0.02ms (5 rows) | 1.6x
|
|
573
|
+
MATCH (s:Service {type: "RPC"}) RETURN s.name | 0.02ms (10 rows) | 0.01ms (10 rows) | 1.8x
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
Each row shows:
|
|
577
|
+
- **Query** — the Cypher query (truncated at 63 characters)
|
|
578
|
+
- **No index** — average time without indexes (full-graph scan)
|
|
579
|
+
- **Indexed** — average time with pre-computed label, property, and edge-type indexes
|
|
580
|
+
- **Speedup** — ratio of no-index time to indexed time
|
|
581
|
+
|
|
582
|
+
### Default queries
|
|
583
|
+
|
|
584
|
+
When no `-q` argument is provided, the benchmark runs these 5 queries:
|
|
585
|
+
|
|
586
|
+
| # | Query | What it tests |
|
|
587
|
+
|---|---|---|
|
|
588
|
+
| 1 | `MATCH (s:Service) RETURN s` | Label-only node lookup |
|
|
589
|
+
| 2 | `MATCH (s:Service)-[r:DEPENDS_ON*1..2]->(d) RETURN s.name, d.name` | Variable-length path traversal with typed edges |
|
|
590
|
+
| 3 | `MATCH (n) RETURN count(n) AS total` | Full-graph scan with aggregation |
|
|
591
|
+
| 4 | `MATCH (s:Service) RETURN s ORDER BY s.name SKIP 2 LIMIT 5` | Label lookup + sorting + pagination |
|
|
592
|
+
| 5 | `MATCH (s:Service {type: "RPC"}) RETURN s.name` | Combined label + property filter |
|
|
593
|
+
|
|
594
|
+
### How it works
|
|
595
|
+
|
|
596
|
+
1. Loads the graph from a JSON file and builds a Graphology graph
|
|
597
|
+
2. Builds pre-computed indexes (label, property, edge-type adjacency) from the same data
|
|
598
|
+
3. For each query, runs two sets of 50 iterations:
|
|
599
|
+
- **No index** — engine receives no indexes, falls back to full-graph scan
|
|
600
|
+
- **Indexed** — engine receives pre-computed indexes for O(1) lookups
|
|
601
|
+
4. Reports per-iteration average time and speedup ratio
|
|
602
|
+
|
|
603
|
+
### Interpretation
|
|
604
|
+
|
|
605
|
+
Speedup ratios vary by query type:
|
|
606
|
+
- **Label-only lookups** benefit most from the label index (2–3x)
|
|
607
|
+
- **Property filters** benefit from the property index (1.5–2x)
|
|
608
|
+
- **Path traversals** benefit from the edge-type adjacency index (2–3x)
|
|
609
|
+
- **Full-graph scans** (no filter) show minimal difference since indexes provide no shortcut
|
|
610
|
+
|
|
611
|
+
On larger graphs (hundreds or thousands of nodes), the speedup from indexes becomes more pronounced as full-graph scans scale linearly with graph size.
|
|
612
|
+
|
|
613
|
+
---
|
|
614
|
+
|
|
615
|
+
## Next Steps
|
|
616
|
+
|
|
617
|
+
- **[Query Guide](query-guide)** — Full Cypher syntax reference and query patterns
|
|
618
|
+
- **[Examples](examples)** — Ready-to-run queries against the bundled cloud infrastructure graph
|