@utaba/deep-memory-storage-cosmosdb 0.16.0 → 0.17.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/LICENSE +15 -4
- package/README.md +262 -0
- package/dist/index.cjs +1269 -671
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +136 -7
- package/dist/index.d.ts +136 -7
- package/dist/index.js +1256 -645
- package/dist/index.js.map +1 -1
- package/package.json +4 -5
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StorageProvider, GraphTraversalProvider, EnsureSchemaResult, GraphTraversalCapabilities } from '@utaba/deep-memory/providers';
|
|
1
|
+
import { StorageProvider, GraphTraversalProvider, EnsureSchemaResult, EntityReadOptions, GraphTraversalCapabilities } from '@utaba/deep-memory/providers';
|
|
2
2
|
import { UsageSink, StorageRepositoryConfig, StoredRepository, RepositoryFilter, PaginatedResult, StoredRepositorySummary, RepositoryUpdate, DeleteProgressCallback, RepositoryStats, MemoryVocabulary, PaginationOptions, VocabularyChangeRecord, StoredEntity, StoredEntityUpdate, StorageFindQuery, StoredRelationship, RelationshipQueryOptions, StorageExploreOptions, StorageNeighborhood, StoragePathOptions, StoragePathResult, StorageTimelineOptions, StorageTimelineResult, ExportChunk, ImportChunk, BulkImportOptions, BulkImportResult, TraversalSpec, TraversalResult } from '@utaba/deep-memory/types';
|
|
3
3
|
import gremlin from 'gremlin';
|
|
4
4
|
|
|
@@ -36,9 +36,17 @@ interface CosmosDbProviderConfig {
|
|
|
36
36
|
*/
|
|
37
37
|
declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvider {
|
|
38
38
|
private readonly conn;
|
|
39
|
+
private readonly docClient;
|
|
39
40
|
private readonly config;
|
|
40
41
|
private readonly compiler;
|
|
41
42
|
private readonly reportUsage;
|
|
43
|
+
/**
|
|
44
|
+
* Per-process vocabulary cache, keyed by repositoryId. Read lazily by
|
|
45
|
+
* `traverseImpl` via `getVocabularyCached`; invalidated on `saveVocabulary`.
|
|
46
|
+
* Each provider instance owns its own cache so isolated test providers do
|
|
47
|
+
* not share state.
|
|
48
|
+
*/
|
|
49
|
+
private readonly vocabularyCache;
|
|
42
50
|
constructor(config: CosmosDbProviderConfig);
|
|
43
51
|
/**
|
|
44
52
|
* Run the given operation inside an async-local usage scope. While the
|
|
@@ -68,6 +76,18 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
|
|
|
68
76
|
dispose(): Promise<void>;
|
|
69
77
|
ensureSchema(): Promise<EnsureSchemaResult>;
|
|
70
78
|
private ensureSchemaImpl;
|
|
79
|
+
/**
|
|
80
|
+
* Step E — verify the container's indexing policy covers every path that
|
|
81
|
+
* the SQL `findEntities` rewrite hits. The probe (2026-05-26) confirmed
|
|
82
|
+
* code-managed containers get the Cosmos default policy (everything
|
|
83
|
+
* indexed). This guard is for operator-facing protection: externally
|
|
84
|
+
* provisioned containers (ARM/Bicep) can have `excludedPaths` set, which
|
|
85
|
+
* would force `findEntities` to scan rather than index-lookup.
|
|
86
|
+
*
|
|
87
|
+
* Single GET against the colls resource, minimal RU. Never fails
|
|
88
|
+
* `ensureSchema` — a diagnostic must not break provisioning.
|
|
89
|
+
*/
|
|
90
|
+
private runIndexingPolicyDiagnostic;
|
|
71
91
|
/** Derive the REST endpoint from config — either explicit or from the Gremlin endpoint host. */
|
|
72
92
|
private getRestEndpoint;
|
|
73
93
|
createRepository(config: StorageRepositoryConfig): Promise<StoredRepository>;
|
|
@@ -81,24 +101,49 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
|
|
|
81
101
|
}>;
|
|
82
102
|
getRepositoryStats(repositoryId: string): Promise<RepositoryStats>;
|
|
83
103
|
getVocabulary(repositoryId: string): Promise<MemoryVocabulary>;
|
|
104
|
+
/**
|
|
105
|
+
* Cached vocabulary read used by traversal compilation. The vocabulary is
|
|
106
|
+
* compile-time context for the GremlinCompiler — it changes on the order of
|
|
107
|
+
* once per session, but the traversal hot path pays one round-trip per call
|
|
108
|
+
* to fetch it. The cache flips that to one round-trip per TTL window.
|
|
109
|
+
*
|
|
110
|
+
* Reads inside an active usage scope are still recorded if a fetch happens
|
|
111
|
+
* (cache miss); cache hits emit no round-trip and therefore no usage entry.
|
|
112
|
+
*/
|
|
113
|
+
private getVocabularyCached;
|
|
114
|
+
/** Drop the cache entry for a repository — call after any vocabulary write. */
|
|
115
|
+
private invalidateVocabularyCache;
|
|
84
116
|
saveVocabulary(repositoryId: string, vocabulary: MemoryVocabulary): Promise<void>;
|
|
85
117
|
getVocabularyChangeLog(repositoryId: string, options?: PaginationOptions): Promise<PaginatedResult<VocabularyChangeRecord>>;
|
|
86
118
|
createEntity(repositoryId: string, entity: StoredEntity): Promise<StoredEntity>;
|
|
87
|
-
getEntity(repositoryId: string, entityId: string): Promise<StoredEntity | null>;
|
|
88
|
-
getEntityBySlug(repositoryId: string, slug: string): Promise<StoredEntity | null>;
|
|
89
|
-
getEntities(repositoryId: string, entityIds: string[]): Promise<Map<string, StoredEntity>>;
|
|
119
|
+
getEntity(repositoryId: string, entityId: string, options?: EntityReadOptions): Promise<StoredEntity | null>;
|
|
120
|
+
getEntityBySlug(repositoryId: string, slug: string, options?: EntityReadOptions): Promise<StoredEntity | null>;
|
|
121
|
+
getEntities(repositoryId: string, entityIds: string[], options?: EntityReadOptions): Promise<Map<string, StoredEntity>>;
|
|
90
122
|
updateEntity(repositoryId: string, entityId: string, updates: StoredEntityUpdate): Promise<StoredEntity>;
|
|
91
123
|
deleteEntity(repositoryId: string, entityId: string): Promise<void>;
|
|
92
124
|
deleteEntities(repositoryId: string, ids: string[]): Promise<{
|
|
93
125
|
deleted: string[];
|
|
94
126
|
notFound: string[];
|
|
95
127
|
}>;
|
|
128
|
+
/**
|
|
129
|
+
* Single round-trip bulk-delete via the aggregate-side-effect pattern:
|
|
130
|
+
* collapses the per-chunk existence-check + drop into one Gremlin call.
|
|
131
|
+
*
|
|
132
|
+
* g.V()...hasId(within(...)).has('entityType')
|
|
133
|
+
* .aggregate('found').by('id') // collects the ids that match
|
|
134
|
+
* .drop() // drops the vertices (and cascaded edges)
|
|
135
|
+
* .cap('found') // emits the bucket as the single result
|
|
136
|
+
*
|
|
137
|
+
* The bucket is always emitted as a single list item — empty when nothing
|
|
138
|
+
* matched (probe-verified 2026-05-25, local-tests/phase7-shape-probe.mjs).
|
|
139
|
+
* `notFound` is derived client-side as the set difference.
|
|
140
|
+
*/
|
|
96
141
|
private deleteEntitiesImpl;
|
|
97
142
|
deleteEntitiesByType(repositoryId: string, entityType: string): Promise<{
|
|
98
143
|
deletedEntities: number;
|
|
99
|
-
deletedRelationships: number;
|
|
144
|
+
deletedRelationships: number | undefined;
|
|
100
145
|
}>;
|
|
101
|
-
findEntities(repositoryId: string, query: StorageFindQuery): Promise<PaginatedResult<StoredEntity>>;
|
|
146
|
+
findEntities(repositoryId: string, query: StorageFindQuery, options?: EntityReadOptions): Promise<PaginatedResult<StoredEntity>>;
|
|
102
147
|
createRelationship(repositoryId: string, relationship: StoredRelationship): Promise<StoredRelationship>;
|
|
103
148
|
getRelationship(repositoryId: string, relationshipId: string): Promise<StoredRelationship | null>;
|
|
104
149
|
getEntityRelationships(repositoryId: string, entityId: string, options?: RelationshipQueryOptions): Promise<PaginatedResult<StoredRelationship>>;
|
|
@@ -107,12 +152,69 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
|
|
|
107
152
|
deleted: string[];
|
|
108
153
|
notFound: string[];
|
|
109
154
|
}>;
|
|
155
|
+
/**
|
|
156
|
+
* Single round-trip bulk relationship delete via the aggregate-side-effect
|
|
157
|
+
* pattern: collapses the per-chunk existence-check + drop into one Gremlin
|
|
158
|
+
* call. Gremlin drop on edges is routed by the engine and the bucket gives
|
|
159
|
+
* back the exact ids that were actually dropped, so `notFound` can be
|
|
160
|
+
* derived client-side.
|
|
161
|
+
*
|
|
162
|
+
* Source-id partition routing is not exposed on this method (the public
|
|
163
|
+
* surface accepts only edge ids), so the lookup may fan out across
|
|
164
|
+
* partitions — see [docs/cosmosdb-gremlin-compatibility.md §`g.E().has`
|
|
165
|
+
* doesn't always push partition down]. Callers that already hold a
|
|
166
|
+
* StoredRelationship and want partition-scoped routing should add a
|
|
167
|
+
* dedicated method when the need is concrete.
|
|
168
|
+
*/
|
|
110
169
|
private deleteRelationshipsImpl;
|
|
111
170
|
deleteRelationshipsByType(repositoryId: string, relationshipType: string): Promise<{
|
|
112
171
|
deletedRelationships: number;
|
|
113
172
|
}>;
|
|
114
173
|
exploreNeighborhood(repositoryId: string, entityId: string, options: StorageExploreOptions): Promise<StorageNeighborhood>;
|
|
174
|
+
/**
|
|
175
|
+
* Compiler-model implementation of exploreNeighborhood.
|
|
176
|
+
*
|
|
177
|
+
* Strategy: for each depth `d` from 1 to `options.depth`, build a
|
|
178
|
+
* cumulative `TraversalSpec` with `d` steps and `returnMode: 'all'`, run
|
|
179
|
+
* it through {@link executeTraversal}, then walk one BFS layer client-side
|
|
180
|
+
* from the previous frontier using the returned edges.
|
|
181
|
+
*
|
|
182
|
+
* The server-side step direction is fixed to `'both'` (catches every edge
|
|
183
|
+
* in either direction). The directional + bidirectional filter is applied
|
|
184
|
+
* client-side per hop — i.e. `direction: 'outbound'` includes inbound edges
|
|
185
|
+
* where `bidirectional` is true. The CosmosDB Gremlin compiler does not
|
|
186
|
+
* natively express the `union(outE, inE.has(bidirectional))` shape that
|
|
187
|
+
* would push this filter server-side, so it stays client-side; the
|
|
188
|
+
* observable contract (which edges count toward "outbound" given
|
|
189
|
+
* bidirectionality) is preserved.
|
|
190
|
+
*
|
|
191
|
+
* Round-trips per call: `options.depth` (one per layer). The previous BFS
|
|
192
|
+
* was `1 + fanout + fanout² + …` round-trips for the same depth.
|
|
193
|
+
*/
|
|
194
|
+
private exploreNeighborhoodImpl;
|
|
115
195
|
findPaths(repositoryId: string, sourceId: string, targetId: string, options: StoragePathOptions): Promise<StoragePathResult>;
|
|
196
|
+
/**
|
|
197
|
+
* Compiler-model implementation of findPaths.
|
|
198
|
+
*
|
|
199
|
+
* Strategy: build a `TraversalSpec` with a single `'both'` direction step
|
|
200
|
+
* in `repeat()` mode with `emitIntermediates: true` and `returnMode:
|
|
201
|
+
* 'path'`, run it once. The compiler always emits `.simplePath()` in path
|
|
202
|
+
* mode for cycle prevention, producing
|
|
203
|
+
* `.emit().repeat(bothE().otherV()).times(maxDepth).simplePath()
|
|
204
|
+
* .range(...).path().by(<v>).by(<e>)`, which yields paths of every length
|
|
205
|
+
* from 0 (the start vertex alone) to `maxDepth`. Live-probed against the
|
|
206
|
+
* Cosmos emulator 2026-05-25 — see local-tests/phase4-repeat-emit-probe.mjs.
|
|
207
|
+
*
|
|
208
|
+
* The pre-Phase-4 Cosmos BFS in (now-deleted) `packages/storage-cosmosdb/
|
|
209
|
+
* src/queries/traversal.ts` traversed edges with unconditional `bothE()`
|
|
210
|
+
* regardless of the `bidirectional` flag (path discovery is reachability,
|
|
211
|
+
* not semantic direction). Mirror that here by using step direction
|
|
212
|
+
* `'both'` and not applying any direction filter. The plan's §6
|
|
213
|
+
* observable-outputs rule requires preserving this.
|
|
214
|
+
*
|
|
215
|
+
* One round-trip total. The previous BFS was up to `1 + fanout + fanout² + …`.
|
|
216
|
+
*/
|
|
217
|
+
private findPathsImpl;
|
|
116
218
|
getTimeline(repositoryId: string, entityId: string, options: StorageTimelineOptions): Promise<StorageTimelineResult>;
|
|
117
219
|
exportAll(repositoryId: string): AsyncIterable<ExportChunk>;
|
|
118
220
|
importBulk(repositoryId: string, data: ImportChunk[], options?: BulkImportOptions): Promise<BulkImportResult>;
|
|
@@ -125,7 +227,34 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
|
|
|
125
227
|
private trackIterable;
|
|
126
228
|
getCapabilities(): GraphTraversalCapabilities;
|
|
127
229
|
traverse(repositoryId: string, spec: TraversalSpec): Promise<TraversalResult>;
|
|
128
|
-
|
|
230
|
+
/**
|
|
231
|
+
* Compile + execute a TraversalSpec against this repository's partition.
|
|
232
|
+
*
|
|
233
|
+
* Internal entrypoint shared by `traverse` (the public surface) and the
|
|
234
|
+
* compiler-model rewrites of `exploreNeighborhood` / `findPaths`. Does NOT
|
|
235
|
+
* wrap in `track()` — the outer public method owns its own usage scope, and
|
|
236
|
+
* inner submits accumulate into that scope (the nested-scope guard in
|
|
237
|
+
* `track()` keeps nested public calls from emitting duplicate records).
|
|
238
|
+
*/
|
|
239
|
+
private traverseInternal;
|
|
240
|
+
/**
|
|
241
|
+
* Lower-level traversal helper: compiles a spec, submits to Gremlin, and
|
|
242
|
+
* parses the rows into raw {@link StoredEntity} / {@link StoredRelationship}
|
|
243
|
+
* objects (no detail-level projection, no provenance stripping). Used by
|
|
244
|
+
* `traverseInternal` and by the storage-layer rewrites of
|
|
245
|
+
* `exploreNeighborhood` / `findPaths` that need the full stored shape to
|
|
246
|
+
* rebuild `StorageNeighborhood` / `StoragePathResult`.
|
|
247
|
+
*
|
|
248
|
+
* Returns a discriminated bag — only the fields relevant to `spec.returnMode`
|
|
249
|
+
* are populated:
|
|
250
|
+
* - `'terminal'`: `terminalEntities` (in row order, no dedup beyond what the
|
|
251
|
+
* server emitted).
|
|
252
|
+
* - `'all'`: `allEntities` and `allRelationships`, already server-deduped.
|
|
253
|
+
* - `'path'`: `pathRows` plus the `entityMap` / `relationshipMap` lookup
|
|
254
|
+
* tables and `pathRelFirstDirection` for the first-seen direction per
|
|
255
|
+
* deduped edge id.
|
|
256
|
+
*/
|
|
257
|
+
private executeTraversal;
|
|
129
258
|
/**
|
|
130
259
|
* Execute a raw Gremlin query with caller-supplied bindings.
|
|
131
260
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StorageProvider, GraphTraversalProvider, EnsureSchemaResult, GraphTraversalCapabilities } from '@utaba/deep-memory/providers';
|
|
1
|
+
import { StorageProvider, GraphTraversalProvider, EnsureSchemaResult, EntityReadOptions, GraphTraversalCapabilities } from '@utaba/deep-memory/providers';
|
|
2
2
|
import { UsageSink, StorageRepositoryConfig, StoredRepository, RepositoryFilter, PaginatedResult, StoredRepositorySummary, RepositoryUpdate, DeleteProgressCallback, RepositoryStats, MemoryVocabulary, PaginationOptions, VocabularyChangeRecord, StoredEntity, StoredEntityUpdate, StorageFindQuery, StoredRelationship, RelationshipQueryOptions, StorageExploreOptions, StorageNeighborhood, StoragePathOptions, StoragePathResult, StorageTimelineOptions, StorageTimelineResult, ExportChunk, ImportChunk, BulkImportOptions, BulkImportResult, TraversalSpec, TraversalResult } from '@utaba/deep-memory/types';
|
|
3
3
|
import gremlin from 'gremlin';
|
|
4
4
|
|
|
@@ -36,9 +36,17 @@ interface CosmosDbProviderConfig {
|
|
|
36
36
|
*/
|
|
37
37
|
declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvider {
|
|
38
38
|
private readonly conn;
|
|
39
|
+
private readonly docClient;
|
|
39
40
|
private readonly config;
|
|
40
41
|
private readonly compiler;
|
|
41
42
|
private readonly reportUsage;
|
|
43
|
+
/**
|
|
44
|
+
* Per-process vocabulary cache, keyed by repositoryId. Read lazily by
|
|
45
|
+
* `traverseImpl` via `getVocabularyCached`; invalidated on `saveVocabulary`.
|
|
46
|
+
* Each provider instance owns its own cache so isolated test providers do
|
|
47
|
+
* not share state.
|
|
48
|
+
*/
|
|
49
|
+
private readonly vocabularyCache;
|
|
42
50
|
constructor(config: CosmosDbProviderConfig);
|
|
43
51
|
/**
|
|
44
52
|
* Run the given operation inside an async-local usage scope. While the
|
|
@@ -68,6 +76,18 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
|
|
|
68
76
|
dispose(): Promise<void>;
|
|
69
77
|
ensureSchema(): Promise<EnsureSchemaResult>;
|
|
70
78
|
private ensureSchemaImpl;
|
|
79
|
+
/**
|
|
80
|
+
* Step E — verify the container's indexing policy covers every path that
|
|
81
|
+
* the SQL `findEntities` rewrite hits. The probe (2026-05-26) confirmed
|
|
82
|
+
* code-managed containers get the Cosmos default policy (everything
|
|
83
|
+
* indexed). This guard is for operator-facing protection: externally
|
|
84
|
+
* provisioned containers (ARM/Bicep) can have `excludedPaths` set, which
|
|
85
|
+
* would force `findEntities` to scan rather than index-lookup.
|
|
86
|
+
*
|
|
87
|
+
* Single GET against the colls resource, minimal RU. Never fails
|
|
88
|
+
* `ensureSchema` — a diagnostic must not break provisioning.
|
|
89
|
+
*/
|
|
90
|
+
private runIndexingPolicyDiagnostic;
|
|
71
91
|
/** Derive the REST endpoint from config — either explicit or from the Gremlin endpoint host. */
|
|
72
92
|
private getRestEndpoint;
|
|
73
93
|
createRepository(config: StorageRepositoryConfig): Promise<StoredRepository>;
|
|
@@ -81,24 +101,49 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
|
|
|
81
101
|
}>;
|
|
82
102
|
getRepositoryStats(repositoryId: string): Promise<RepositoryStats>;
|
|
83
103
|
getVocabulary(repositoryId: string): Promise<MemoryVocabulary>;
|
|
104
|
+
/**
|
|
105
|
+
* Cached vocabulary read used by traversal compilation. The vocabulary is
|
|
106
|
+
* compile-time context for the GremlinCompiler — it changes on the order of
|
|
107
|
+
* once per session, but the traversal hot path pays one round-trip per call
|
|
108
|
+
* to fetch it. The cache flips that to one round-trip per TTL window.
|
|
109
|
+
*
|
|
110
|
+
* Reads inside an active usage scope are still recorded if a fetch happens
|
|
111
|
+
* (cache miss); cache hits emit no round-trip and therefore no usage entry.
|
|
112
|
+
*/
|
|
113
|
+
private getVocabularyCached;
|
|
114
|
+
/** Drop the cache entry for a repository — call after any vocabulary write. */
|
|
115
|
+
private invalidateVocabularyCache;
|
|
84
116
|
saveVocabulary(repositoryId: string, vocabulary: MemoryVocabulary): Promise<void>;
|
|
85
117
|
getVocabularyChangeLog(repositoryId: string, options?: PaginationOptions): Promise<PaginatedResult<VocabularyChangeRecord>>;
|
|
86
118
|
createEntity(repositoryId: string, entity: StoredEntity): Promise<StoredEntity>;
|
|
87
|
-
getEntity(repositoryId: string, entityId: string): Promise<StoredEntity | null>;
|
|
88
|
-
getEntityBySlug(repositoryId: string, slug: string): Promise<StoredEntity | null>;
|
|
89
|
-
getEntities(repositoryId: string, entityIds: string[]): Promise<Map<string, StoredEntity>>;
|
|
119
|
+
getEntity(repositoryId: string, entityId: string, options?: EntityReadOptions): Promise<StoredEntity | null>;
|
|
120
|
+
getEntityBySlug(repositoryId: string, slug: string, options?: EntityReadOptions): Promise<StoredEntity | null>;
|
|
121
|
+
getEntities(repositoryId: string, entityIds: string[], options?: EntityReadOptions): Promise<Map<string, StoredEntity>>;
|
|
90
122
|
updateEntity(repositoryId: string, entityId: string, updates: StoredEntityUpdate): Promise<StoredEntity>;
|
|
91
123
|
deleteEntity(repositoryId: string, entityId: string): Promise<void>;
|
|
92
124
|
deleteEntities(repositoryId: string, ids: string[]): Promise<{
|
|
93
125
|
deleted: string[];
|
|
94
126
|
notFound: string[];
|
|
95
127
|
}>;
|
|
128
|
+
/**
|
|
129
|
+
* Single round-trip bulk-delete via the aggregate-side-effect pattern:
|
|
130
|
+
* collapses the per-chunk existence-check + drop into one Gremlin call.
|
|
131
|
+
*
|
|
132
|
+
* g.V()...hasId(within(...)).has('entityType')
|
|
133
|
+
* .aggregate('found').by('id') // collects the ids that match
|
|
134
|
+
* .drop() // drops the vertices (and cascaded edges)
|
|
135
|
+
* .cap('found') // emits the bucket as the single result
|
|
136
|
+
*
|
|
137
|
+
* The bucket is always emitted as a single list item — empty when nothing
|
|
138
|
+
* matched (probe-verified 2026-05-25, local-tests/phase7-shape-probe.mjs).
|
|
139
|
+
* `notFound` is derived client-side as the set difference.
|
|
140
|
+
*/
|
|
96
141
|
private deleteEntitiesImpl;
|
|
97
142
|
deleteEntitiesByType(repositoryId: string, entityType: string): Promise<{
|
|
98
143
|
deletedEntities: number;
|
|
99
|
-
deletedRelationships: number;
|
|
144
|
+
deletedRelationships: number | undefined;
|
|
100
145
|
}>;
|
|
101
|
-
findEntities(repositoryId: string, query: StorageFindQuery): Promise<PaginatedResult<StoredEntity>>;
|
|
146
|
+
findEntities(repositoryId: string, query: StorageFindQuery, options?: EntityReadOptions): Promise<PaginatedResult<StoredEntity>>;
|
|
102
147
|
createRelationship(repositoryId: string, relationship: StoredRelationship): Promise<StoredRelationship>;
|
|
103
148
|
getRelationship(repositoryId: string, relationshipId: string): Promise<StoredRelationship | null>;
|
|
104
149
|
getEntityRelationships(repositoryId: string, entityId: string, options?: RelationshipQueryOptions): Promise<PaginatedResult<StoredRelationship>>;
|
|
@@ -107,12 +152,69 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
|
|
|
107
152
|
deleted: string[];
|
|
108
153
|
notFound: string[];
|
|
109
154
|
}>;
|
|
155
|
+
/**
|
|
156
|
+
* Single round-trip bulk relationship delete via the aggregate-side-effect
|
|
157
|
+
* pattern: collapses the per-chunk existence-check + drop into one Gremlin
|
|
158
|
+
* call. Gremlin drop on edges is routed by the engine and the bucket gives
|
|
159
|
+
* back the exact ids that were actually dropped, so `notFound` can be
|
|
160
|
+
* derived client-side.
|
|
161
|
+
*
|
|
162
|
+
* Source-id partition routing is not exposed on this method (the public
|
|
163
|
+
* surface accepts only edge ids), so the lookup may fan out across
|
|
164
|
+
* partitions — see [docs/cosmosdb-gremlin-compatibility.md §`g.E().has`
|
|
165
|
+
* doesn't always push partition down]. Callers that already hold a
|
|
166
|
+
* StoredRelationship and want partition-scoped routing should add a
|
|
167
|
+
* dedicated method when the need is concrete.
|
|
168
|
+
*/
|
|
110
169
|
private deleteRelationshipsImpl;
|
|
111
170
|
deleteRelationshipsByType(repositoryId: string, relationshipType: string): Promise<{
|
|
112
171
|
deletedRelationships: number;
|
|
113
172
|
}>;
|
|
114
173
|
exploreNeighborhood(repositoryId: string, entityId: string, options: StorageExploreOptions): Promise<StorageNeighborhood>;
|
|
174
|
+
/**
|
|
175
|
+
* Compiler-model implementation of exploreNeighborhood.
|
|
176
|
+
*
|
|
177
|
+
* Strategy: for each depth `d` from 1 to `options.depth`, build a
|
|
178
|
+
* cumulative `TraversalSpec` with `d` steps and `returnMode: 'all'`, run
|
|
179
|
+
* it through {@link executeTraversal}, then walk one BFS layer client-side
|
|
180
|
+
* from the previous frontier using the returned edges.
|
|
181
|
+
*
|
|
182
|
+
* The server-side step direction is fixed to `'both'` (catches every edge
|
|
183
|
+
* in either direction). The directional + bidirectional filter is applied
|
|
184
|
+
* client-side per hop — i.e. `direction: 'outbound'` includes inbound edges
|
|
185
|
+
* where `bidirectional` is true. The CosmosDB Gremlin compiler does not
|
|
186
|
+
* natively express the `union(outE, inE.has(bidirectional))` shape that
|
|
187
|
+
* would push this filter server-side, so it stays client-side; the
|
|
188
|
+
* observable contract (which edges count toward "outbound" given
|
|
189
|
+
* bidirectionality) is preserved.
|
|
190
|
+
*
|
|
191
|
+
* Round-trips per call: `options.depth` (one per layer). The previous BFS
|
|
192
|
+
* was `1 + fanout + fanout² + …` round-trips for the same depth.
|
|
193
|
+
*/
|
|
194
|
+
private exploreNeighborhoodImpl;
|
|
115
195
|
findPaths(repositoryId: string, sourceId: string, targetId: string, options: StoragePathOptions): Promise<StoragePathResult>;
|
|
196
|
+
/**
|
|
197
|
+
* Compiler-model implementation of findPaths.
|
|
198
|
+
*
|
|
199
|
+
* Strategy: build a `TraversalSpec` with a single `'both'` direction step
|
|
200
|
+
* in `repeat()` mode with `emitIntermediates: true` and `returnMode:
|
|
201
|
+
* 'path'`, run it once. The compiler always emits `.simplePath()` in path
|
|
202
|
+
* mode for cycle prevention, producing
|
|
203
|
+
* `.emit().repeat(bothE().otherV()).times(maxDepth).simplePath()
|
|
204
|
+
* .range(...).path().by(<v>).by(<e>)`, which yields paths of every length
|
|
205
|
+
* from 0 (the start vertex alone) to `maxDepth`. Live-probed against the
|
|
206
|
+
* Cosmos emulator 2026-05-25 — see local-tests/phase4-repeat-emit-probe.mjs.
|
|
207
|
+
*
|
|
208
|
+
* The pre-Phase-4 Cosmos BFS in (now-deleted) `packages/storage-cosmosdb/
|
|
209
|
+
* src/queries/traversal.ts` traversed edges with unconditional `bothE()`
|
|
210
|
+
* regardless of the `bidirectional` flag (path discovery is reachability,
|
|
211
|
+
* not semantic direction). Mirror that here by using step direction
|
|
212
|
+
* `'both'` and not applying any direction filter. The plan's §6
|
|
213
|
+
* observable-outputs rule requires preserving this.
|
|
214
|
+
*
|
|
215
|
+
* One round-trip total. The previous BFS was up to `1 + fanout + fanout² + …`.
|
|
216
|
+
*/
|
|
217
|
+
private findPathsImpl;
|
|
116
218
|
getTimeline(repositoryId: string, entityId: string, options: StorageTimelineOptions): Promise<StorageTimelineResult>;
|
|
117
219
|
exportAll(repositoryId: string): AsyncIterable<ExportChunk>;
|
|
118
220
|
importBulk(repositoryId: string, data: ImportChunk[], options?: BulkImportOptions): Promise<BulkImportResult>;
|
|
@@ -125,7 +227,34 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
|
|
|
125
227
|
private trackIterable;
|
|
126
228
|
getCapabilities(): GraphTraversalCapabilities;
|
|
127
229
|
traverse(repositoryId: string, spec: TraversalSpec): Promise<TraversalResult>;
|
|
128
|
-
|
|
230
|
+
/**
|
|
231
|
+
* Compile + execute a TraversalSpec against this repository's partition.
|
|
232
|
+
*
|
|
233
|
+
* Internal entrypoint shared by `traverse` (the public surface) and the
|
|
234
|
+
* compiler-model rewrites of `exploreNeighborhood` / `findPaths`. Does NOT
|
|
235
|
+
* wrap in `track()` — the outer public method owns its own usage scope, and
|
|
236
|
+
* inner submits accumulate into that scope (the nested-scope guard in
|
|
237
|
+
* `track()` keeps nested public calls from emitting duplicate records).
|
|
238
|
+
*/
|
|
239
|
+
private traverseInternal;
|
|
240
|
+
/**
|
|
241
|
+
* Lower-level traversal helper: compiles a spec, submits to Gremlin, and
|
|
242
|
+
* parses the rows into raw {@link StoredEntity} / {@link StoredRelationship}
|
|
243
|
+
* objects (no detail-level projection, no provenance stripping). Used by
|
|
244
|
+
* `traverseInternal` and by the storage-layer rewrites of
|
|
245
|
+
* `exploreNeighborhood` / `findPaths` that need the full stored shape to
|
|
246
|
+
* rebuild `StorageNeighborhood` / `StoragePathResult`.
|
|
247
|
+
*
|
|
248
|
+
* Returns a discriminated bag — only the fields relevant to `spec.returnMode`
|
|
249
|
+
* are populated:
|
|
250
|
+
* - `'terminal'`: `terminalEntities` (in row order, no dedup beyond what the
|
|
251
|
+
* server emitted).
|
|
252
|
+
* - `'all'`: `allEntities` and `allRelationships`, already server-deduped.
|
|
253
|
+
* - `'path'`: `pathRows` plus the `entityMap` / `relationshipMap` lookup
|
|
254
|
+
* tables and `pathRelFirstDirection` for the first-seen direction per
|
|
255
|
+
* deduped edge id.
|
|
256
|
+
*/
|
|
257
|
+
private executeTraversal;
|
|
129
258
|
/**
|
|
130
259
|
* Execute a raw Gremlin query with caller-supplied bindings.
|
|
131
260
|
*
|