grafio-mongo 3.1.0 → 3.3.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.
@@ -7,6 +7,10 @@ export interface QueryOptionsFilterProperty {
7
7
  AND?: QueryOptionsFilterProperty[];
8
8
  OR?: QueryOptionsFilterProperty[];
9
9
  }
10
+ import { GraphError } from 'grafio/errors';
11
+ export declare class IndexAlreadyExistsError extends GraphError {
12
+ constructor(indexName: string);
13
+ }
10
14
  export interface MongoStorageProviderOptions {
11
15
  graphId?: string;
12
16
  nodesCollection?: string;
@@ -53,8 +57,19 @@ export declare class MongoStorageProvider implements IStorageProvider {
53
57
  deleteEdge(id: string, transaction?: ITransactionHandle): Promise<void>;
54
58
  exportJSON(): Promise<GraphData>;
55
59
  importJSON(data: GraphData): Promise<void>;
56
- createIndex(target: 'node' | 'edge', propertyKey: string): Promise<void>;
57
- hasIndex(target: 'node' | 'edge', propertyKey: string): Promise<boolean>;
60
+ createIndex(name: string, target: 'node' | 'edge', propertyKeys: string[]): Promise<void>;
61
+ hasIndex(target: 'node' | 'edge', propertyKeys: string[]): Promise<boolean>;
62
+ getIndex(name: string): Promise<{
63
+ name: string;
64
+ target: 'node' | 'edge';
65
+ propertyKeys: string[];
66
+ } | undefined>;
67
+ deleteIndex(name: string): Promise<void>;
68
+ getIndexes(): Promise<{
69
+ name: string;
70
+ target: 'node' | 'edge';
71
+ propertyKeys: string[];
72
+ }[]>;
58
73
  addProperty(target: 'node' | 'edge', id: string, key: string, value: unknown, transaction?: ITransactionHandle): Promise<void>;
59
74
  updateProperty(target: 'node' | 'edge', id: string, key: string, value: unknown, transaction?: ITransactionHandle): Promise<void>;
60
75
  deleteProperty(target: 'node' | 'edge', id: string, key: string, transaction?: ITransactionHandle): Promise<void>;
@@ -1,9 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MongoStorageProvider = void 0;
3
+ exports.MongoStorageProvider = exports.IndexAlreadyExistsError = void 0;
4
4
  const mongodb_1 = require("mongodb");
5
5
  const grafio_1 = require("grafio");
6
6
  const errors_1 = require("grafio/errors");
7
+ class IndexAlreadyExistsError extends errors_1.GraphError {
8
+ constructor(indexName) {
9
+ super(`Index '${indexName}' already exists`);
10
+ this.name = 'IndexAlreadyExistsError';
11
+ }
12
+ }
13
+ exports.IndexAlreadyExistsError = IndexAlreadyExistsError;
7
14
  class MongoStorageProvider {
8
15
  _nodes;
9
16
  _edges;
@@ -35,6 +42,8 @@ class MongoStorageProvider {
35
42
  await this._edges.createIndex({ graphId: 1, targetId: 1, type: 1 }, { name: 'edge_graph_target_type' });
36
43
  }
37
44
  async clear() {
45
+ const indexes = await this.getIndexes();
46
+ await Promise.all(indexes.map(index => this.deleteIndex(index.name)));
38
47
  await Promise.all([
39
48
  this._nodes.deleteMany({ graphId: this._graphId }),
40
49
  this._edges.deleteMany({ graphId: this._graphId }),
@@ -373,42 +382,107 @@ class MongoStorageProvider {
373
382
  }
374
383
  }
375
384
  }
376
- async createIndex(target, propertyKey) {
377
- if (target === 'node') {
378
- const indexFields = { graphId: 1, [`properties.${propertyKey}`]: 1 };
379
- await this._nodes.createIndex(indexFields, {
380
- name: `node_graphId_${propertyKey}`,
381
- background: true
382
- });
383
- await this._nodes.createIndex({ ...indexFields, type: 1 }, {
384
- name: `node_graphId_type_${propertyKey}`,
385
- background: true
386
- });
385
+ async createIndex(name, target, propertyKeys) {
386
+ const sortedPropertyKeys = [...propertyKeys].sort();
387
+ const metadataCollection = this._nodes.db.collection('grafio_index_metadata');
388
+ const existing = await metadataCollection.findOne({ name, graphId: this._graphId });
389
+ if (existing) {
390
+ throw new IndexAlreadyExistsError(name);
387
391
  }
388
- else {
389
- const indexFields = { graphId: 1, [`properties.${propertyKey}`]: 1 };
390
- await this._edges.createIndex(indexFields, {
391
- name: `edge_graphId_${propertyKey}`,
392
- background: true
393
- });
394
- await this._edges.createIndex({ ...indexFields, type: 1 }, {
395
- name: `edge_graphId_type_${propertyKey}`,
396
- background: true
397
- });
392
+ await metadataCollection.insertOne({
393
+ name,
394
+ graphId: this._graphId,
395
+ target,
396
+ propertyKeys: sortedPropertyKeys,
397
+ createdOn: Date.now()
398
+ });
399
+ for (const propertyKey of sortedPropertyKeys) {
400
+ if (target === 'node') {
401
+ const indexFields = { graphId: 1, [`properties.${propertyKey}`]: 1 };
402
+ await this._nodes.createIndex(indexFields, {
403
+ name: `${name}_node_graphId_${propertyKey}`,
404
+ background: true
405
+ });
406
+ await this._nodes.createIndex({ ...indexFields, type: 1 }, {
407
+ name: `${name}_node_graphId_type_${propertyKey}`,
408
+ background: true
409
+ });
410
+ }
411
+ else {
412
+ const indexFields = { graphId: 1, [`properties.${propertyKey}`]: 1 };
413
+ await this._edges.createIndex(indexFields, {
414
+ name: `${name}_edge_graphId_${propertyKey}`,
415
+ background: true
416
+ });
417
+ await this._edges.createIndex({ ...indexFields, type: 1 }, {
418
+ name: `${name}_edge_graphId_type_${propertyKey}`,
419
+ background: true
420
+ });
421
+ }
398
422
  }
399
423
  }
400
- async hasIndex(target, propertyKey) {
401
- const collection = target === 'node' ? this._nodes : this._edges;
402
- const indexNameWithoutType = `${target === 'node' ? 'node' : 'edge'}_graphId_${propertyKey}`;
403
- const indexNameWithType = `${target === 'node' ? 'node' : 'edge'}_graphId_type_${propertyKey}`;
404
- for await (const index of collection.listIndexes()) {
405
- const name = index.name;
406
- if (name === indexNameWithoutType || name === indexNameWithType) {
424
+ async hasIndex(target, propertyKeys) {
425
+ const sortedPropertyKeys = [...propertyKeys].sort();
426
+ const metadataCollection = this._nodes.db.collection('grafio_index_metadata');
427
+ const cursor = metadataCollection.find({ graphId: this._graphId, target });
428
+ for await (const metadata of cursor) {
429
+ const indexPropertyKeys = metadata.propertyKeys;
430
+ const sortedIndexPropertyKeys = [...indexPropertyKeys].sort();
431
+ const hasAllKeys = sortedPropertyKeys.every(key => sortedIndexPropertyKeys.includes(key));
432
+ if (hasAllKeys) {
407
433
  return true;
408
434
  }
409
435
  }
410
436
  return false;
411
437
  }
438
+ async getIndex(name) {
439
+ const metadataCollection = this._nodes.db.collection('grafio_index_metadata');
440
+ const metadata = await metadataCollection.findOne({ name, graphId: this._graphId });
441
+ if (!metadata) {
442
+ return undefined;
443
+ }
444
+ return {
445
+ name: metadata.name,
446
+ target: metadata.target,
447
+ propertyKeys: metadata.propertyKeys
448
+ };
449
+ }
450
+ async deleteIndex(name) {
451
+ const metadataCollection = this._nodes.db.collection('grafio_index_metadata');
452
+ const metadata = await metadataCollection.findOne({ name, graphId: this._graphId });
453
+ if (!metadata) {
454
+ throw new Error(`Index with name '${name}' does not exist`);
455
+ }
456
+ const target = metadata.target;
457
+ const propertyKeys = metadata.propertyKeys;
458
+ const collection = target === 'node' ? this._nodes : this._edges;
459
+ for (const propertyKey of propertyKeys) {
460
+ try {
461
+ await collection.dropIndex(`${name}_${target === 'node' ? 'node' : 'edge'}_graphId_${propertyKey}`);
462
+ }
463
+ catch {
464
+ }
465
+ try {
466
+ await collection.dropIndex(`${name}_${target === 'node' ? 'node' : 'edge'}_graphId_type_${propertyKey}`);
467
+ }
468
+ catch {
469
+ }
470
+ }
471
+ await metadataCollection.deleteOne({ name, graphId: this._graphId });
472
+ }
473
+ async getIndexes() {
474
+ const metadataCollection = this._nodes.db.collection('grafio_index_metadata');
475
+ const cursor = metadataCollection.find({ graphId: this._graphId });
476
+ const indexes = [];
477
+ for await (const metadata of cursor) {
478
+ indexes.push({
479
+ name: metadata.name,
480
+ target: metadata.target,
481
+ propertyKeys: metadata.propertyKeys
482
+ });
483
+ }
484
+ return indexes;
485
+ }
412
486
  async addProperty(target, id, key, value, transaction) {
413
487
  if (!(0, grafio_1.isPrimitive)(value)) {
414
488
  throw new errors_1.InvalidPropertyError(key, value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grafio-mongo",
3
- "version": "3.1.0",
3
+ "version": "3.3.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.1.0",
19
+ "grafio": ">=7.2.0",
20
20
  "mongodb": ">=5.0.0"
21
21
  },
22
22
  "devDependencies": {