66 VertexProperties ,
77 EdgeProperties ,
88 AnyEdgePropertyName ,
9+ AnyVertexPropertyName ,
910} from "./GraphSchema.js" ;
1011import { ElementId } from "./GraphStorage.js" ;
1112import {
@@ -108,7 +109,11 @@ export class TraversalPath<
108109 public with < const TValue , const TLabels extends readonly string [ ] = [ ] > (
109110 value : TValue ,
110111 labels : TLabels = [ ] as unknown as TLabels ,
111- ) : this extends TraversalPath < any , unknown , any > ? TraversalPath < this, TValue , TLabels > : never {
112+ ) : this extends TraversalPath < any , unknown , any >
113+ ? TLabels extends readonly [ ]
114+ ? this
115+ : TraversalPath < this, TValue , TLabels >
116+ : this {
112117 return new TraversalPath ( this as any , value , labels ) as any ;
113118 }
114119
@@ -129,20 +134,96 @@ export class TraversalPath<
129134 return undefined as PropertyOf < TValue > [ TPropertyName ] ;
130135 }
131136
137+ /**
138+ * Return the vertices in this path, in traversal order.
139+ * Optionally extract a property from each vertex instead of returning the vertices themselves.
140+ */
141+ public nodes ( ) : GetTraversalPathVertices < TraversalPath < TParent , TValue , TLabels > > ;
142+ public nodes <
143+ const TPropertyName extends AnyVertexPropertyNameOrString <
144+ GetTraversalPathSchema < TraversalPath < TParent , TValue , TLabels > >
145+ > ,
146+ > (
147+ propertyName : TPropertyName ,
148+ ) : GetTraversalPathVertexPropertyValues < TraversalPath < TParent , TValue , TLabels > , TPropertyName > ;
149+ public nodes ( propertyName ?: string ) {
150+ const nodes : unknown [ ] = [ ] ;
151+ for ( const step of this ) {
152+ if ( ! ( step . value instanceof Vertex ) ) {
153+ continue ;
154+ }
155+ nodes . push ( propertyName ? step . property ( propertyName as never ) : step . value ) ;
156+ }
157+ return nodes ;
158+ }
159+
160+ /**
161+ * Return the edges/relationships in this path, in traversal order.
162+ * Optionally extract a property from each edge instead of returning the edges themselves.
163+ */
164+ public relationships ( ) : GetTraversalPathEdges < TraversalPath < TParent , TValue , TLabels > > ;
165+ public relationships <
166+ const TPropertyName extends AnyEdgePropertyNameOrString <
167+ GetTraversalPathSchema < TraversalPath < TParent , TValue , TLabels > >
168+ > ,
169+ > (
170+ propertyName : TPropertyName ,
171+ ) : GetTraversalPathEdgePropertyValues < TraversalPath < TParent , TValue , TLabels > , TPropertyName > ;
172+ public relationships ( propertyName ?: string ) {
173+ const relationships : unknown [ ] = [ ] ;
174+ for ( const step of this ) {
175+ if ( ! ( step . value instanceof Edge ) ) {
176+ continue ;
177+ }
178+ relationships . push ( propertyName ? step . property ( propertyName as never ) : step . value ) ;
179+ }
180+ return relationships ;
181+ }
182+
183+ /**
184+ * Return the number of edges/relationships in this path.
185+ * Mirrors Cypher's `length(path)`.
186+ */
187+ public length ( ) : number {
188+ let edgeCount = 0 ;
189+ for ( const step of this ) {
190+ if ( step . value instanceof Edge ) {
191+ edgeCount ++ ;
192+ }
193+ }
194+ return edgeCount ;
195+ }
196+
197+ /**
198+ * Sum a numeric property across all items in the path.
199+ * Non-numeric or missing values are ignored.
200+ */
201+ public sum ( propertyName : string ) : number {
202+ let total = 0 ;
203+ for ( const step of this ) {
204+ const value = step . property ( propertyName as never ) ;
205+ if ( typeof value === "number" ) {
206+ total += value ;
207+ }
208+ }
209+ return total ;
210+ }
211+
132212 /**
133213 * Get a node in the path by label.
134214 * @param label The label to get the node by.
135215 */
136- public get < const TLabel extends string > ( label : TLabel ) : GetTraversalPathByLabel < this, TLabel > {
137- // eslint-disable-next-line @typescript-eslint/no-this-alias
138- let node : TraversalPath < any , unknown , any > = this ;
216+ public get < const TLabel extends string > (
217+ label : TLabel ,
218+ ) : GetTraversalPathByLabel < TraversalPath < TParent , TValue , TLabels > , TLabel > {
219+ let node : TraversalPath < any , any , any > = this ;
139220 while ( node !== undefined ) {
140221 if ( node . labels . includes ( label ) ) {
141- return node as GetTraversalPathByLabel < this , TLabel > ;
222+ return node as GetTraversalPathByLabel < TraversalPath < TParent , TValue , TLabels > , TLabel > ;
142223 }
143224 node = node . parent ;
144225 }
145- return undefined as GetTraversalPathByLabel < this , TLabel > ;
226+ return undefined as GetTraversalPathByLabel < TraversalPath < TParent , TValue , TLabels > , TLabel > ;
146227 }
147228
148229 /**
@@ -151,17 +232,16 @@ export class TraversalPath<
151232 */
152233 public getAll < const TLabel extends string > (
153234 label : TLabel ,
154- ) : GetAllTraversalPathsByLabel < this, TLabel > {
155- const nodes = [ ] as TraversalPath < any , unknown , any > [ ] ;
156- // eslint-disable-next-line @typescript-eslint/no-this-alias
157- let node : TraversalPath < any , unknown , any > = this ;
235+ ) : GetAllTraversalPathsByLabel < TraversalPath < TParent , TValue , TLabels > , TLabel > {
236+ const nodes = [ ] as TraversalPath < any , any , any > [ ] ;
237+ let node : TraversalPath < any , any , any > = this ;
158238 while ( node !== undefined ) {
159239 if ( node . labels . includes ( label ) ) {
160240 nodes . unshift ( node ) ;
161241 }
162242 node = node . parent ;
163243 }
164- return nodes as GetAllTraversalPathsByLabel < this , TLabel > ;
244+ return nodes as GetAllTraversalPathsByLabel < TraversalPath < TParent , TValue , TLabels > , TLabel > ;
165245 }
166246
167247 public toJSON ( ) : TraversalPathJSON {
@@ -224,6 +304,62 @@ export class TraversalPath<
224304
225305type PropertyOf < T > = T extends Element < any , any , infer TProperties , any > ? TProperties : keyof T ;
226306
307+ type GetTraversalPathItems < TPath > =
308+ TPath extends TraversalPath < infer TParent , infer TValue , any >
309+ ? [ ...GetTraversalPathItems < TParent > , TValue ]
310+ : [ ] ;
311+
312+ type GetTraversalPathSchema < TPath > =
313+ Extract < GetTraversalPathItems < TPath > [ number ] , Element < any , any , any , any > > extends Element <
314+ infer TSchema ,
315+ any ,
316+ any ,
317+ any
318+ >
319+ ? TSchema
320+ : GraphSchema ;
321+
322+ type AnyVertexPropertyNameOrString < TSchema extends GraphSchema > =
323+ AnyVertexPropertyName < TSchema > extends never ? string : AnyVertexPropertyName < TSchema > ;
324+
325+ type AnyEdgePropertyNameOrString < TSchema extends GraphSchema > =
326+ AnyEdgePropertyName < TSchema > extends never ? string : AnyEdgePropertyName < TSchema > ;
327+
328+ type AnyVertexPropertyValue < TSchema extends GraphSchema , TPropertyName extends string > = {
329+ [ TVertexLabel in VertexLabel < TSchema > ] : TPropertyName extends keyof VertexProperties <
330+ TSchema ,
331+ TVertexLabel
332+ >
333+ ? VertexProperties < TSchema , TVertexLabel > [ TPropertyName ]
334+ : never ;
335+ } [ VertexLabel < TSchema > ] ;
336+
337+ type AnyEdgePropertyValue < TSchema extends GraphSchema , TPropertyName extends string > = {
338+ [ TEdgeLabel in EdgeLabel < TSchema > ] : TPropertyName extends keyof EdgeProperties <
339+ TSchema ,
340+ TEdgeLabel
341+ >
342+ ? EdgeProperties < TSchema , TEdgeLabel > [ TPropertyName ]
343+ : never ;
344+ } [ EdgeLabel < TSchema > ] ;
345+
346+ type GetTraversalPathVertices < TPath > = Extract <
347+ GetTraversalPathItems < TPath > [ number ] ,
348+ Vertex < any , any >
349+ > [ ] ;
350+
351+ type GetTraversalPathEdges < TPath > = Extract < GetTraversalPathItems < TPath > [ number ] , Edge < any , any > > [ ] ;
352+
353+ type GetTraversalPathVertexPropertyValues <
354+ TPath ,
355+ TPropertyName extends string ,
356+ > = AnyVertexPropertyValue < GetTraversalPathSchema < TPath > , TPropertyName > [ ] ;
357+
358+ type GetTraversalPathEdgePropertyValues < TPath , TPropertyName extends string > = AnyEdgePropertyValue <
359+ GetTraversalPathSchema < TPath > ,
360+ TPropertyName
361+ > [ ] ;
362+
227363type GetTraversalPathValue < TPath > =
228364 TPath extends TraversalPath < any , infer TValue , any >
229365 ? TValue
0 commit comments