grafio-mongo 3.0.0 → 3.1.0
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/README.md +11 -393
- package/dist/MongoStorageProvider.d.ts +1 -0
- package/dist/MongoStorageProvider.js +9 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
# grafio-mongo
|
|
2
2
|
|
|
3
|
-
MongoDB storage backend for
|
|
3
|
+
MongoDB storage backend for **grafio** — a graph database with pluggable storage architecture.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
This package provides the MongoDB storage provider for grafio, extracted from the core project. It enables **persistent storage** for grafio graphs using MongoDB (>= 5.0.0), with optimized indexes for nodes and edges and native transaction support.
|
|
5
|
+
**Full documentation**: [https://satya-jugran.github.io/grafio](https://satya-jugran.github.io/grafio)
|
|
8
6
|
|
|
9
7
|
## Features
|
|
10
8
|
|
|
11
|
-
- **MongoDB Backend** —
|
|
12
|
-
- **Multiple
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **Graph Factories** — `MongoGraphFactory` for controlled instance creation
|
|
9
|
+
- **MongoDB Backend** — Persistent storage with MongoDB (>= 5.0.0)
|
|
10
|
+
- **Multiple Graphs** — via `graphId` partitioning
|
|
11
|
+
- **Native Transactions** — Atomic multi-operation updates
|
|
12
|
+
- **Optimized Indexes** — For nodes and edges
|
|
16
13
|
|
|
17
14
|
## Installation
|
|
18
15
|
|
|
@@ -26,399 +23,20 @@ npm install grafio-mongo
|
|
|
26
23
|
import { MongoClient } from 'mongodb';
|
|
27
24
|
import { MongoGraphFactory } from 'grafio-mongo';
|
|
28
25
|
|
|
29
|
-
// Connect to MongoDB
|
|
30
26
|
const client = new MongoClient('mongodb://localhost:27017');
|
|
31
27
|
await client.connect();
|
|
32
28
|
|
|
33
29
|
const factory = new MongoGraphFactory(client.db('mydb'));
|
|
34
|
-
|
|
35
|
-
// Create indexes once at startup (idempotent — safe to call every time)
|
|
36
30
|
await factory.ensureIndexes();
|
|
37
31
|
|
|
38
|
-
// Get a graph scoped to a named partition
|
|
39
32
|
const graph = factory.forGraph('my-graph');
|
|
33
|
+
graph.addNode('Person', { name: 'Alice', age: 30 });
|
|
34
|
+
graph.addNode('City', { name: 'New York' });
|
|
35
|
+
graph.addEdge('LIVES_IN', 'alice', 'nyc', { since: 2020 });
|
|
40
36
|
|
|
41
|
-
// Add nodes and edges
|
|
42
|
-
const alice = await graph.addNode('Person', { name: 'Alice' });
|
|
43
|
-
const bob = await graph.addNode('Person', { name: 'Bob' });
|
|
44
|
-
await graph.addEdge(alice.id, bob.id, 'KNOWS');
|
|
45
|
-
|
|
46
|
-
// Navigate the graph
|
|
47
|
-
const path = await graph.traverse(alice.id, bob.id, { edgeTypes: ['KNOWS'] });
|
|
48
|
-
|
|
49
|
-
// Caller manages the MongoClient lifecycle
|
|
50
37
|
await client.close();
|
|
51
38
|
```
|
|
52
39
|
|
|
53
|
-
##
|
|
54
|
-
|
|
55
|
-
Factory class for creating MongoDB-backed Graph instances:
|
|
56
|
-
|
|
57
|
-
```typescript
|
|
58
|
-
import { MongoGraphFactory } from 'grafio-mongo';
|
|
59
|
-
|
|
60
|
-
const factory = new MongoGraphFactory(db);
|
|
61
|
-
await factory.ensureIndexes();
|
|
62
|
-
|
|
63
|
-
const graph = factory.forGraph('my-graph');
|
|
64
|
-
// or with custom options
|
|
65
|
-
const graph2 = factory.forGraph('custom-graph', {
|
|
66
|
-
nodesCollection: 'my_nodes', // default: 'sgdb_nodes'
|
|
67
|
-
edgesCollection: 'my_edges', // default: 'sgdb_edges'
|
|
68
|
-
});
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### Factory Options
|
|
72
|
-
|
|
73
|
-
| Option | Type | Default | Description |
|
|
74
|
-
|--------|------|---------|-------------|
|
|
75
|
-
| `nodesCollection` | `string` | `'sgdb_nodes'` | Collection name for nodes |
|
|
76
|
-
| `edgesCollection` | `string` | `'sgdb_edges'` | Collection name for edges |
|
|
77
|
-
|
|
78
|
-
## Direct MongoStorageProvider Usage
|
|
79
|
-
|
|
80
|
-
For fine-grained control over collection names and graph partitioning:
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
83
|
-
import { MongoClient } from 'mongodb';
|
|
84
|
-
import { Graph, MongoStorageProvider } from 'grafio-mongo';
|
|
85
|
-
|
|
86
|
-
const client = new MongoClient('mongodb://localhost:27017');
|
|
87
|
-
await client.connect();
|
|
88
|
-
|
|
89
|
-
const provider = new MongoStorageProvider(client.db('mydb'), {
|
|
90
|
-
graphId: 'my-graph', // default: 'default' — partitions data by graph id
|
|
91
|
-
nodesCollection: 'my_nodes', // default: 'sgdb_nodes'
|
|
92
|
-
edgesCollection: 'my_edges', // default: 'sgdb_edges'
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
await provider.ensureIndexes();
|
|
96
|
-
|
|
97
|
-
const graph = new Graph(provider);
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
## Indexes
|
|
101
|
-
|
|
102
|
-
The `ensureIndexes()` method creates the following indexes for optimized queries:
|
|
103
|
-
|
|
104
|
-
### Nodes Collection
|
|
105
|
-
|
|
106
|
-
| Index | Purpose |
|
|
107
|
-
|-------|---------|
|
|
108
|
-
| `{ graphId: 1, id: 1 }` unique | Fast node id lookups within a graph partition |
|
|
109
|
-
| `{ graphId: 1, type: 1 }` | `getNodesByType()` within a graph partition |
|
|
110
|
-
| `{ graphId: 1, properties: 1 }` | Property value lookups within a graph partition |
|
|
111
|
-
|
|
112
|
-
### Edges Collection
|
|
113
|
-
|
|
114
|
-
| Index | Purpose |
|
|
115
|
-
|-------|---------|
|
|
116
|
-
| `{ graphId: 1, id: 1 }` unique | Fast edge id lookups within a graph partition |
|
|
117
|
-
| `{ graphId: 1, type: 1 }` | `getEdgesByType()` within a graph partition |
|
|
118
|
-
| `{ graphId: 1, sourceId: 1, type: 1 }` | Outgoing adjacency queries |
|
|
119
|
-
| `{ graphId: 1, targetId: 1, type: 1 }` | Incoming adjacency queries |
|
|
120
|
-
|
|
121
|
-
## Transactions
|
|
122
|
-
|
|
123
|
-
MongoDB storage provider supports native transactions via MongoDB sessions:
|
|
124
|
-
|
|
125
|
-
```typescript
|
|
126
|
-
import { Graph, GraphTransaction } from 'grafio';
|
|
127
|
-
|
|
128
|
-
const graph = factory.forGraph('my-graph');
|
|
129
|
-
const txn = graph.createTransaction();
|
|
130
|
-
await txn.begin();
|
|
131
|
-
|
|
132
|
-
try {
|
|
133
|
-
const alice = await graph.addNode('Person', { name: 'Alice' }, txn);
|
|
134
|
-
const bob = await graph.addNode('Person', { name: 'Bob' }, txn);
|
|
135
|
-
await graph.addEdge(alice.id, bob.id, 'KNOWS', {}, txn);
|
|
136
|
-
await txn.commit();
|
|
137
|
-
} catch (error) {
|
|
138
|
-
if (txn.isActive()) {
|
|
139
|
-
await txn.rollback();
|
|
140
|
-
}
|
|
141
|
-
throw error;
|
|
142
|
-
}
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
**Note:** MongoDB storage provider requires a replica set for transaction support.
|
|
146
|
-
|
|
147
|
-
### Transaction Lifecycle
|
|
148
|
-
|
|
149
|
-
- `begin()` — starts a new transaction
|
|
150
|
-
- `commit()` — applies all changes atomically (throws if transaction failed)
|
|
151
|
-
- `rollback()` — discards all changes
|
|
152
|
-
- `isFailed()` — returns true if a storage operation failed within the transaction
|
|
153
|
-
- `isActive()` — returns true if transaction is active and not failed
|
|
154
|
-
|
|
155
|
-
## Cypher Query Language
|
|
156
|
-
|
|
157
|
-
MongoDB-backed graphs support read-only openCypher-compatible queries via the `CypherEngine`:
|
|
158
|
-
|
|
159
|
-
```typescript
|
|
160
|
-
import { Graph } from 'grafio';
|
|
161
|
-
import { CypherEngine } from 'grafio/cypher';
|
|
162
|
-
|
|
163
|
-
const graph = factory.forGraph('my-graph');
|
|
164
|
-
|
|
165
|
-
// Build your graph
|
|
166
|
-
const alice = await graph.addNode('Person', { name: 'Alice', age: 30 });
|
|
167
|
-
const bob = await graph.addNode('Person', { name: 'Bob', age: 25 });
|
|
168
|
-
await graph.addEdge(alice.id, bob.id, 'KNOWS', { since: 2020 });
|
|
169
|
-
|
|
170
|
-
const engine = new CypherEngine(graph);
|
|
171
|
-
|
|
172
|
-
// Scan nodes by type
|
|
173
|
-
const result = await engine.query('MATCH (p:Person) RETURN p.name, p.age');
|
|
174
|
-
|
|
175
|
-
// Filter with WHERE
|
|
176
|
-
const adults = await engine.query(
|
|
177
|
-
'MATCH (p:Person) WHERE p.age > 25 RETURN p.name'
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
// Follow relationships
|
|
181
|
-
const friends = await engine.query(
|
|
182
|
-
'MATCH (a:Person)-[:KNOWS]->(b:Person) RETURN a.name, b.name'
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
// Multi-hop traversal
|
|
186
|
-
const network = await engine.query(
|
|
187
|
-
'MATCH (a:Person)-[:KNOWS*1..2]->(b:Person) RETURN DISTINCT a.name, b.name'
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
// Parameterized queries
|
|
191
|
-
const byName = await engine.query(
|
|
192
|
-
'MATCH (p:Person {name: $name}) RETURN p',
|
|
193
|
-
{ name: 'Alice' }
|
|
194
|
-
);
|
|
195
|
-
|
|
196
|
-
// Pagination
|
|
197
|
-
const page = await engine.query(
|
|
198
|
-
'MATCH (p:Person) RETURN p ORDER BY p.age DESC SKIP 0 LIMIT 10'
|
|
199
|
-
);
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### Supported Clauses
|
|
203
|
-
|
|
204
|
-
| Clause | Support | Notes |
|
|
205
|
-
|--------|---------|-------|
|
|
206
|
-
| `MATCH` | ✅ Read-only patterns | Typed/untyped nodes, directed edges, multi-label `(n:A\|B)`, inline property maps |
|
|
207
|
-
| `WHERE` | ✅ Full expressions | `AND`/`OR`/`NOT`, comparisons, `IN`, `NOT IN`, `IS NULL`, `IS NOT NULL` |
|
|
208
|
-
| `RETURN` | ✅ With `DISTINCT` | Property access, aliases with `AS` |
|
|
209
|
-
| `ORDER BY` | ✅ ASC/DESC | Default ASC when omitted |
|
|
210
|
-
| `SKIP` | ✅ Literal + `$param` | Evaluated at runtime |
|
|
211
|
-
| `LIMIT` | ✅ Literal + `$param` | Evaluated at runtime |
|
|
212
|
-
| `CREATE` / `DELETE` / `SET` / `REMOVE` / `MERGE` | ❌ Rejected | Validation gate prevents execution |
|
|
213
|
-
|
|
214
|
-
#### Aggregation Functions
|
|
215
|
-
|
|
216
|
-
```typescript
|
|
217
|
-
// Basic count
|
|
218
|
-
const total = await engine.query('MATCH (p:Person) RETURN COUNT(p) AS total');
|
|
219
|
-
|
|
220
|
-
// Group by with aggregation
|
|
221
|
-
const byCity = await engine.query(
|
|
222
|
-
'MATCH (p:Person) RETURN p.city, COUNT(*) AS cnt ORDER BY cnt DESC'
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
// HAVING clause
|
|
226
|
-
const popular = await engine.query(
|
|
227
|
-
'MATCH (p:Person) RETURN p.city, COUNT(*) AS cnt HAVING cnt > 1'
|
|
228
|
-
);
|
|
229
|
-
|
|
230
|
-
// Multiple aggregates
|
|
231
|
-
const stats = await engine.query(
|
|
232
|
-
'MATCH (p:Person) RETURN MIN(p.age), MAX(p.age), AVG(p.age)'
|
|
233
|
-
);
|
|
234
|
-
|
|
235
|
-
// Named path variable
|
|
236
|
-
const paths = await engine.query(
|
|
237
|
-
'MATCH p = (a:Person)-[:KNOWS]->(b:Person)-[:KNOWS]->(c:Person) RETURN p'
|
|
238
|
-
);
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
#### Query Plan — Inspect Execution Steps
|
|
242
|
-
|
|
243
|
-
Use `getQueryPlan()` to inspect the logical execution plan for a query without running it:
|
|
244
|
-
|
|
245
|
-
```typescript
|
|
246
|
-
// Get query plan in JSON format (default)
|
|
247
|
-
const planJson = await engine.getQueryPlan('MATCH (p:Person) RETURN p.name');
|
|
248
|
-
// Returns: { plan: { steps: [...] } }
|
|
249
|
-
|
|
250
|
-
// Get in Text tree format
|
|
251
|
-
const planText = await engine.getQueryPlan('MATCH (p:Person)-[:KNOWS]->(b) RETURN p.name, b.name', undefined, 'text');
|
|
252
|
-
/*
|
|
253
|
-
NodeScanStep (Person)
|
|
254
|
-
EdgeExpandStep (KNOWS, outgoing)
|
|
255
|
-
ProjectStep [p.name, b.name]
|
|
256
|
-
*/
|
|
257
|
-
|
|
258
|
-
// Get in Mermaid flowchart format
|
|
259
|
-
const planMermaid = await engine.getQueryPlan('MATCH (p:Person)-[:KNOWS*1..2]->(b) RETURN p.name', undefined, 'mermaid');
|
|
260
|
-
/*
|
|
261
|
-
flowchart TD
|
|
262
|
-
Step1[NodeScanStep Person]
|
|
263
|
-
Step2[EdgeExpandStep KNOWS, 1..2 hops, outgoing]
|
|
264
|
-
Step3[ProjectStep [p.name]]
|
|
265
|
-
Step1 --> Step2
|
|
266
|
-
Step2 --> Step3
|
|
267
|
-
*/
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
#### Execution Plan — Query Plan with Runtime Statistics
|
|
271
|
-
|
|
272
|
-
Use `execute()` with the `executionPlan` option to get the query plan enriched with per-step timing and row counts:
|
|
273
|
-
|
|
274
|
-
```typescript
|
|
275
|
-
const result = await engine.execute(
|
|
276
|
-
'MATCH (p:Person)-[:KNOWS]->(b:Person) RETURN p.name, b.name',
|
|
277
|
-
{},
|
|
278
|
-
{ executionPlan: { format: 'json' } }
|
|
279
|
-
);
|
|
280
|
-
|
|
281
|
-
// result.executionPlan contains the formatted plan with stats
|
|
282
|
-
// result.summary contains timing metadata
|
|
283
|
-
// result.summary.planExecutionStats contains per-step timing data
|
|
284
|
-
|
|
285
|
-
// Get execution plan in Text format with timing
|
|
286
|
-
const execText = await engine.execute(
|
|
287
|
-
'MATCH (a:Person)-[:KNOWS]->(b:Person)-[:KNOWS]->(c:Person) RETURN a.name',
|
|
288
|
-
{},
|
|
289
|
-
{ executionPlan: { format: 'text' } }
|
|
290
|
-
);
|
|
291
|
-
console.log(execText.executionPlan);
|
|
292
|
-
/*
|
|
293
|
-
NodeScanStep Person (1ms, 33.3%, 2 rows)
|
|
294
|
-
EdgeExpandStep KNOWS, outgoing (1ms, 33.3%, 5 rows)
|
|
295
|
-
EdgeExpandStep KNOWS, outgoing (1ms, 33.3%, 3 rows)
|
|
296
|
-
ProjectStep [a.name]
|
|
297
|
-
*/
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
Supported formats: `'json'`, `'text'`, `'mermaid'`
|
|
301
|
-
|
|
302
|
-
The execution plan includes:
|
|
303
|
-
- **timeMs**: Time spent in each step
|
|
304
|
-
- **percentageOfTotal**: Percentage of total query time
|
|
305
|
-
- **rowsOut**: Number of rows output by each step
|
|
306
|
-
|
|
307
|
-
### Variable-length Edge Syntax
|
|
308
|
-
|
|
309
|
-
| Syntax | Meaning |
|
|
310
|
-
|--------|---------|
|
|
311
|
-
| `[*]` | Unbounded (up to 100 hops) |
|
|
312
|
-
| `[*1..3]` | 1 to 3 hops (BFS by default) |
|
|
313
|
-
| `[*2]` | Exactly 2 hops |
|
|
314
|
-
| `[*..5]` | Up to 5 hops |
|
|
315
|
-
|
|
316
|
-
> **Strategy selection**: BFS is used by default for multi-hop expansion. When `LIMIT` is present, DFS is selected automatically for better early-result performance.
|
|
317
|
-
|
|
318
|
-
#### Property Filter Operators
|
|
319
|
-
|
|
320
|
-
The `filter.properties` option supports various comparison operators:
|
|
321
|
-
|
|
322
|
-
| Operator | Syntax | Description |
|
|
323
|
-
|----------|--------|-------------|
|
|
324
|
-
| `=` | `{ key: 'age', value: 30 }` | Equality (default) |
|
|
325
|
-
| `<>` | `{ key: 'age', value: 30, op: '<>' }` | Not equal |
|
|
326
|
-
| `>` | `{ key: 'age', value: 25, op: '>' }` | Greater than |
|
|
327
|
-
| `<` | `{ key: 'age', value: 25, op: '<' }` | Less than |
|
|
328
|
-
| `>=` | `{ key: 'age', value: 25, op: '>=' }` | Greater than or equal |
|
|
329
|
-
| `<=` | `{ key: 'age', value: 25, op: '<=' }` | Less than or equal |
|
|
330
|
-
| `CONTAINS` | `{ key: 'name', value: 'John', op: 'CONTAINS' }` | String contains |
|
|
331
|
-
| `STARTS_WITH` | `{ key: 'name', value: 'J', op: 'STARTS_WITH' }` | String prefix |
|
|
332
|
-
| `ENDS_WITH` | `{ key: 'name', value: 'n', op: 'ENDS_WITH' }` | String suffix |
|
|
333
|
-
| `IN` | `{ key: 'city', value: ['NYC', 'LA'], op: 'IN' }` | In array |
|
|
334
|
-
| `NOT_IN` | `{ key: 'city', value: ['SF', 'CHI'], op: 'NOT_IN' }` | Not in array |
|
|
335
|
-
| `IS_NULL` | `{ key: 'age', op: 'IS_NULL' }` | Is null |
|
|
336
|
-
| `IS_NOT_NULL` | `{ key: 'age', op: 'IS_NOT_NULL' }` | Is not null |
|
|
337
|
-
|
|
338
|
-
**AND/OR Chaining:**
|
|
339
|
-
|
|
340
|
-
```typescript
|
|
341
|
-
// AND chaining
|
|
342
|
-
const result = await graph.getNodes({
|
|
343
|
-
filter: {
|
|
344
|
-
AND: [
|
|
345
|
-
{ key: 'age', value: 25, op: '>' },
|
|
346
|
-
{ key: 'city', value: 'NYC' }
|
|
347
|
-
]
|
|
348
|
-
}
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
// OR chaining
|
|
352
|
-
const result = await graph.getNodes({
|
|
353
|
-
filter: {
|
|
354
|
-
OR: [
|
|
355
|
-
{ key: 'city', value: 'NYC' },
|
|
356
|
-
{ key: 'city', value: 'LA' }
|
|
357
|
-
]
|
|
358
|
-
}
|
|
359
|
-
});
|
|
360
|
-
```
|
|
361
|
-
|
|
362
|
-
## Graph Operations
|
|
363
|
-
|
|
364
|
-
All graph operations from grafio are available when using MongoDB storage. See the [grafio documentation](https://github.com/satyajugran/grafio) for the complete API reference.
|
|
365
|
-
|
|
366
|
-
### Example Operations
|
|
367
|
-
|
|
368
|
-
```typescript
|
|
369
|
-
const graph = factory.forGraph('my-graph');
|
|
370
|
-
|
|
371
|
-
// Node operations
|
|
372
|
-
const alice = await graph.addNode('Person', { name: 'Alice', age: 30 });
|
|
373
|
-
const bob = await graph.addNode('Person', { name: 'Bob' });
|
|
374
|
-
await graph.addEdge(alice.id, bob.id, 'KNOWS');
|
|
375
|
-
|
|
376
|
-
// Navigation
|
|
377
|
-
const parents = await graph.getParents(bob.id);
|
|
378
|
-
const children = await graph.getChildren(alice.id);
|
|
379
|
-
|
|
380
|
-
// Traversal
|
|
381
|
-
const path = await graph.traverse(alice.id, bob.id, { method: 'bfs' });
|
|
382
|
-
|
|
383
|
-
// Type filtering
|
|
384
|
-
const allPersons = await graph.getNodesByType('Person');
|
|
385
|
-
|
|
386
|
-
// Property queries with operators
|
|
387
|
-
const adults = await graph.getNodes({ filter: { properties: [{ key: 'age', value: 25, op: '>' }] } });
|
|
388
|
-
|
|
389
|
-
// DAG check and topological sort
|
|
390
|
-
const isDag = await graph.isDAG();
|
|
391
|
-
const order = await graph.topologicalSort();
|
|
392
|
-
|
|
393
|
-
// Export/Import
|
|
394
|
-
const data = await graph.exportJSON();
|
|
395
|
-
await Graph.importJSON(data, new MongoStorageProvider(db, { graphId: 'restored' }));
|
|
396
|
-
```
|
|
397
|
-
|
|
398
|
-
## Development
|
|
399
|
-
|
|
400
|
-
```bash
|
|
401
|
-
# Install dependencies
|
|
402
|
-
npm install
|
|
403
|
-
|
|
404
|
-
# Build TypeScript
|
|
405
|
-
npm run build
|
|
406
|
-
|
|
407
|
-
# Run tests
|
|
408
|
-
npm test
|
|
409
|
-
|
|
410
|
-
# Run tests with coverage
|
|
411
|
-
npm run test:coverage
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
## Testing
|
|
415
|
-
|
|
416
|
-
The test suite runs against the MongoDB backend using `mongodb-memory-server` for integration testing.
|
|
417
|
-
|
|
418
|
-
### Test Structure
|
|
40
|
+
## License
|
|
419
41
|
|
|
420
|
-
|
|
421
|
-
- `tests/EducationGraph.mongo.test.ts` — Education domain graph via MongoDB
|
|
422
|
-
- `tests/SocialGraph.mongo.test.ts` — Social network graph via MongoDB
|
|
423
|
-
- `tests/storage/MongoGraphFactory.test.ts` — Factory lifecycle tests
|
|
424
|
-
- `tests/storage/MongoStorageProvider.test.ts` — Provider unit tests
|
|
42
|
+
GPL 3.0
|
|
@@ -26,6 +26,7 @@ export declare class MongoStorageProvider implements IStorageProvider {
|
|
|
26
26
|
deleteNode(id: string, transaction?: ITransactionHandle): Promise<void>;
|
|
27
27
|
hasNode(id: string, transaction?: ITransactionHandle): Promise<boolean>;
|
|
28
28
|
getNode(id: string, transaction?: ITransactionHandle): Promise<NodeData | undefined>;
|
|
29
|
+
getNodesByIds(ids: string[], transaction?: ITransactionHandle): Promise<Map<string, NodeData>>;
|
|
29
30
|
getNodeCount(options?: StorageQueryOptions): Promise<number>;
|
|
30
31
|
aggregateNodeProperty(key: string, options?: StorageQueryOptions): Promise<{
|
|
31
32
|
count: number;
|
|
@@ -79,6 +79,15 @@ class MongoStorageProvider {
|
|
|
79
79
|
const doc = await this._nodes.findOne({ graphId: this._graphId, id }, { session });
|
|
80
80
|
return doc ? this._docToNode(doc) : undefined;
|
|
81
81
|
}
|
|
82
|
+
async getNodesByIds(ids, transaction) {
|
|
83
|
+
const session = transaction?.context;
|
|
84
|
+
const cursor = this._nodes.find({ graphId: this._graphId, id: { $in: ids } }, { session });
|
|
85
|
+
const nodes = new Map();
|
|
86
|
+
for await (const doc of cursor) {
|
|
87
|
+
nodes.set(doc.id, this._docToNode(doc));
|
|
88
|
+
}
|
|
89
|
+
return nodes;
|
|
90
|
+
}
|
|
82
91
|
async getNodeCount(options) {
|
|
83
92
|
const session = options?.transaction?.context;
|
|
84
93
|
const filter = this._buildNodeFilter(options);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grafio-mongo",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "MongoDB storage backend for grafio",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"grafio": ">=7.
|
|
19
|
+
"grafio": ">=7.1.0",
|
|
20
20
|
"mongodb": ">=5.0.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|