@utaba/deep-memory-storage-cosmosdb 0.6.0 → 0.7.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/dist/index.d.cts CHANGED
@@ -66,6 +66,10 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
66
66
  getEntities(repositoryId: string, entityIds: string[]): Promise<Map<string, StoredEntity>>;
67
67
  updateEntity(repositoryId: string, entityId: string, updates: StoredEntityUpdate): Promise<StoredEntity>;
68
68
  deleteEntity(repositoryId: string, entityId: string): Promise<void>;
69
+ deleteEntities(repositoryId: string, ids: string[]): Promise<{
70
+ deleted: string[];
71
+ notFound: string[];
72
+ }>;
69
73
  deleteEntitiesByType(repositoryId: string, entityType: string): Promise<{
70
74
  deletedEntities: number;
71
75
  deletedRelationships: number;
@@ -75,6 +79,10 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
75
79
  getRelationship(repositoryId: string, relationshipId: string): Promise<StoredRelationship | null>;
76
80
  getEntityRelationships(repositoryId: string, entityId: string, options?: RelationshipQueryOptions): Promise<PaginatedResult<StoredRelationship>>;
77
81
  deleteRelationship(repositoryId: string, relationshipId: string): Promise<void>;
82
+ deleteRelationships(repositoryId: string, ids: string[]): Promise<{
83
+ deleted: string[];
84
+ notFound: string[];
85
+ }>;
78
86
  deleteRelationshipsByType(repositoryId: string, relationshipType: string): Promise<{
79
87
  deletedRelationships: number;
80
88
  }>;
package/dist/index.d.ts CHANGED
@@ -66,6 +66,10 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
66
66
  getEntities(repositoryId: string, entityIds: string[]): Promise<Map<string, StoredEntity>>;
67
67
  updateEntity(repositoryId: string, entityId: string, updates: StoredEntityUpdate): Promise<StoredEntity>;
68
68
  deleteEntity(repositoryId: string, entityId: string): Promise<void>;
69
+ deleteEntities(repositoryId: string, ids: string[]): Promise<{
70
+ deleted: string[];
71
+ notFound: string[];
72
+ }>;
69
73
  deleteEntitiesByType(repositoryId: string, entityType: string): Promise<{
70
74
  deletedEntities: number;
71
75
  deletedRelationships: number;
@@ -75,6 +79,10 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
75
79
  getRelationship(repositoryId: string, relationshipId: string): Promise<StoredRelationship | null>;
76
80
  getEntityRelationships(repositoryId: string, entityId: string, options?: RelationshipQueryOptions): Promise<PaginatedResult<StoredRelationship>>;
77
81
  deleteRelationship(repositoryId: string, relationshipId: string): Promise<void>;
82
+ deleteRelationships(repositoryId: string, ids: string[]): Promise<{
83
+ deleted: string[];
84
+ notFound: string[];
85
+ }>;
78
86
  deleteRelationshipsByType(repositoryId: string, relationshipType: string): Promise<{
79
87
  deletedRelationships: number;
80
88
  }>;
package/dist/index.js CHANGED
@@ -635,12 +635,19 @@ async function updateEntity(conn, repositoryId, entityId, updates) {
635
635
  bindings[paramName] = value;
636
636
  propParts.push(`.property('${key}', ${paramName})`);
637
637
  };
638
+ const dropProp = (key) => {
639
+ propParts.push(`.sideEffect(properties('${key}').drop())`);
640
+ };
641
+ if (updates.entityType !== void 0) addProp("entityType", updates.entityType);
638
642
  if (updates.label !== void 0) addProp("entityLabel", updates.label);
639
643
  if (updates.slug !== void 0) addProp("slug", updates.slug);
640
- if (updates.summary !== void 0) addProp("summary", updates.summary);
644
+ if (updates.summary === null) dropProp("summary");
645
+ else if (updates.summary !== void 0) addProp("summary", updates.summary);
641
646
  if (updates.properties !== void 0) addProp("properties", JSON.stringify(updates.properties));
642
- if (updates.data !== void 0) addProp("data", updates.data);
643
- if (updates.dataFormat !== void 0) addProp("dataFormat", updates.dataFormat);
647
+ if (updates.data === null) dropProp("data");
648
+ else if (updates.data !== void 0) addProp("data", updates.data);
649
+ if (updates.dataFormat === null) dropProp("dataFormat");
650
+ else if (updates.dataFormat !== void 0) addProp("dataFormat", updates.dataFormat);
644
651
  if (updates.embedding !== void 0) addProp("embedding", JSON.stringify(updates.embedding));
645
652
  addProp("modifiedBy", updates.provenance.modifiedBy);
646
653
  addProp("modifiedByType", updates.provenance.modifiedByType);
@@ -688,9 +695,37 @@ async function findEntities(conn, repositoryId, query) {
688
695
  });
689
696
  filterClause += `.has('entityType', within(${typeParams.join(", ")}))`;
690
697
  }
691
- if (query.searchTerm) {
692
- bindings["searchTerm"] = query.searchTerm.toLowerCase();
693
- filterClause += ".has('slug', containing(searchTerm))";
698
+ const hasPropertyFilter = query.properties != null && Object.keys(query.properties).length > 0;
699
+ const needsClientFilter = Boolean(query.searchTerm) || hasPropertyFilter;
700
+ if (needsClientFilter) {
701
+ const dataResult2 = await conn.submit(
702
+ `g.V()${filterClause}.valueMap(true)`,
703
+ bindings
704
+ );
705
+ let items2 = dataResult2.items.map(entityFromGremlin);
706
+ if (query.searchTerm) {
707
+ const term = query.searchTerm.toLowerCase();
708
+ items2 = items2.filter(
709
+ (e) => e.label.toLowerCase().includes(term) || e.slug.toLowerCase().includes(term) || e.summary != null && e.summary.toLowerCase().includes(term)
710
+ );
711
+ }
712
+ if (hasPropertyFilter) {
713
+ items2 = items2.filter((entity) => {
714
+ for (const [key, value] of Object.entries(query.properties)) {
715
+ if (entity.properties[key] !== value) return false;
716
+ }
717
+ return true;
718
+ });
719
+ }
720
+ const total2 = items2.length;
721
+ const paged = items2.slice(query.offset, query.offset + query.limit);
722
+ return {
723
+ items: paged,
724
+ total: total2,
725
+ hasMore: query.offset + query.limit < total2,
726
+ limit: query.limit,
727
+ offset: query.offset
728
+ };
694
729
  }
695
730
  const countResult = await conn.submit(
696
731
  `g.V()${filterClause}.count()`,
@@ -703,15 +738,7 @@ async function findEntities(conn, repositoryId, query) {
703
738
  `g.V()${filterClause}.range(rangeStart, rangeEnd).valueMap(true)`,
704
739
  bindings
705
740
  );
706
- let items = dataResult.items.map(entityFromGremlin);
707
- if (query.properties && Object.keys(query.properties).length > 0) {
708
- items = items.filter((entity) => {
709
- for (const [key, value] of Object.entries(query.properties)) {
710
- if (entity.properties[key] !== value) return false;
711
- }
712
- return true;
713
- });
714
- }
741
+ const items = dataResult.items.map(entityFromGremlin);
715
742
  return {
716
743
  items,
717
744
  total,
@@ -1448,6 +1475,45 @@ var CosmosDbProvider = class {
1448
1475
  this.assertValidRepositoryId(repositoryId);
1449
1476
  return deleteEntity(this.conn, repositoryId, entityId);
1450
1477
  }
1478
+ async deleteEntities(repositoryId, ids) {
1479
+ if (ids.length === 0) return { deleted: [], notFound: [] };
1480
+ this.assertValidRepositoryId(repositoryId);
1481
+ const deleted = [];
1482
+ const CHUNK = 100;
1483
+ for (let i = 0; i < ids.length; i += CHUNK) {
1484
+ const chunk = ids.slice(i, i + CHUNK);
1485
+ const bindings = { rid: repositoryId };
1486
+ const idParams = [];
1487
+ for (let j = 0; j < chunk.length; j++) {
1488
+ const p = `id${j}`;
1489
+ bindings[p] = chunk[j];
1490
+ idParams.push(p);
1491
+ }
1492
+ const withinExpr = `P.within(${idParams.join(", ")})`;
1493
+ const existResult = await this.conn.submit(
1494
+ `g.V().has('repositoryId', rid).has('id', ${withinExpr}).has('entityType').values('id')`,
1495
+ bindings
1496
+ );
1497
+ const found = existResult.items;
1498
+ if (found.length > 0) {
1499
+ const dropBindings = { rid: repositoryId };
1500
+ const dropIdParams = [];
1501
+ for (let j = 0; j < found.length; j++) {
1502
+ const p = `did${j}`;
1503
+ dropBindings[p] = found[j];
1504
+ dropIdParams.push(p);
1505
+ }
1506
+ const dropWithinExpr = `P.within(${dropIdParams.join(", ")})`;
1507
+ await this.conn.submit(
1508
+ `g.V().has('repositoryId', rid).has('id', ${dropWithinExpr}).has('entityType').drop()`,
1509
+ dropBindings
1510
+ );
1511
+ deleted.push(...found);
1512
+ }
1513
+ }
1514
+ const deletedSet = new Set(deleted);
1515
+ return { deleted, notFound: ids.filter((id) => !deletedSet.has(id)) };
1516
+ }
1451
1517
  async deleteEntitiesByType(repositoryId, entityType) {
1452
1518
  this.assertValidRepositoryId(repositoryId);
1453
1519
  return deleteEntitiesByType(this.conn, repositoryId, entityType);
@@ -1473,6 +1539,42 @@ var CosmosDbProvider = class {
1473
1539
  this.assertValidRepositoryId(repositoryId);
1474
1540
  return deleteRelationship(this.conn, repositoryId, relationshipId);
1475
1541
  }
1542
+ async deleteRelationships(repositoryId, ids) {
1543
+ if (ids.length === 0) return { deleted: [], notFound: [] };
1544
+ this.assertValidRepositoryId(repositoryId);
1545
+ const deleted = [];
1546
+ const CHUNK = 100;
1547
+ for (let i = 0; i < ids.length; i += CHUNK) {
1548
+ const chunk = ids.slice(i, i + CHUNK);
1549
+ const bindings = { rid: repositoryId };
1550
+ const idParams = [];
1551
+ for (let j = 0; j < chunk.length; j++) {
1552
+ const p = `id${j}`;
1553
+ bindings[p] = chunk[j];
1554
+ idParams.push(p);
1555
+ }
1556
+ const withinExpr = `P.within(${idParams.join(", ")})`;
1557
+ const existResult = await this.conn.submit(
1558
+ `g.E().has('repositoryId', rid).has('id', ${withinExpr}).values('id')`,
1559
+ bindings
1560
+ );
1561
+ const found = existResult.items;
1562
+ if (found.length > 0) {
1563
+ await cosmosRestBatchDelete(
1564
+ this.getRestEndpoint(),
1565
+ this.config.key,
1566
+ this.config.database,
1567
+ this.config.container,
1568
+ repositoryId,
1569
+ found,
1570
+ this.config.rejectUnauthorized ?? true
1571
+ );
1572
+ deleted.push(...found);
1573
+ }
1574
+ }
1575
+ const deletedSet = new Set(deleted);
1576
+ return { deleted, notFound: ids.filter((id) => !deletedSet.has(id)) };
1577
+ }
1476
1578
  async deleteRelationshipsByType(repositoryId, relationshipType) {
1477
1579
  this.assertValidRepositoryId(repositoryId);
1478
1580
  return deleteRelationshipsByType(this.conn, repositoryId, relationshipType);
@@ -1686,6 +1788,32 @@ async function cosmosRestPut(restBase, key, urlPath, resourceLink, resourceType,
1686
1788
  const text = await response.text();
1687
1789
  throw new Error(`CosmosDB REST ${response.status}: ${text}`);
1688
1790
  }
1791
+ async function cosmosRestBatchDelete(restBase, key, database, container, partitionKeyValue, ids, rejectUnauthorized) {
1792
+ const resourceLink = `dbs/${database}/colls/${container}`;
1793
+ const date = (/* @__PURE__ */ new Date()).toUTCString();
1794
+ const token = cosmosAuthToken("post", "docs", resourceLink, date, key);
1795
+ if (!rejectUnauthorized) {
1796
+ process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0";
1797
+ }
1798
+ const ops = ids.map((id) => ({ operationType: "Delete", id }));
1799
+ const response = await fetch(`${restBase}/${resourceLink}/docs`, {
1800
+ method: "POST",
1801
+ headers: {
1802
+ "Authorization": token,
1803
+ "x-ms-version": "2020-07-15",
1804
+ "x-ms-date": date,
1805
+ "Content-Type": "application/json",
1806
+ "x-ms-documentdb-partitionkey": JSON.stringify([partitionKeyValue]),
1807
+ "x-ms-cosmos-is-batch-request": "true",
1808
+ "x-ms-cosmos-batch-atomic": "true"
1809
+ },
1810
+ body: JSON.stringify(ops)
1811
+ });
1812
+ if (response.status !== 200 && response.status !== 207) {
1813
+ const text = await response.text();
1814
+ throw new ProviderError(`CosmosDB batch delete failed (${response.status}): ${text}`);
1815
+ }
1816
+ }
1689
1817
  export {
1690
1818
  CosmosDbConnection,
1691
1819
  CosmosDbProvider