@nshiab/simple-data-analysis-core 2.1.6 → 2.1.7

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.
Files changed (57) hide show
  1. package/esm/class/SimpleTable.d.ts +249 -23
  2. package/esm/class/SimpleTable.d.ts.map +1 -1
  3. package/esm/class/SimpleTable.js +243 -23
  4. package/esm/helpers/graphRouteResultSchema.d.ts +1 -1
  5. package/esm/helpers/graphRouteResultSchema.d.ts.map +1 -1
  6. package/esm/helpers/graphRouteResultSchema.js +3 -2
  7. package/esm/helpers/graphRouteResultSelect.d.ts +1 -1
  8. package/esm/helpers/graphRouteResultSelect.d.ts.map +1 -1
  9. package/esm/helpers/graphRouteResultSelect.js +4 -1
  10. package/esm/helpers/prepareGraphMetricOptions.d.ts +14 -0
  11. package/esm/helpers/prepareGraphMetricOptions.d.ts.map +1 -0
  12. package/esm/helpers/prepareGraphMetricOptions.js +40 -0
  13. package/esm/helpers/prepareGraphTemporalSql.d.ts +3 -0
  14. package/esm/helpers/prepareGraphTemporalSql.d.ts.map +1 -1
  15. package/esm/helpers/prepareGraphTemporalSql.js +7 -0
  16. package/esm/methods/distances.d.ts +2 -0
  17. package/esm/methods/distances.d.ts.map +1 -1
  18. package/esm/methods/distances.js +98 -6
  19. package/esm/methods/findCycles.d.ts +1 -0
  20. package/esm/methods/findCycles.d.ts.map +1 -1
  21. package/esm/methods/findCycles.js +22 -15
  22. package/esm/methods/paths.d.ts +1 -0
  23. package/esm/methods/paths.d.ts.map +1 -1
  24. package/esm/methods/paths.js +21 -14
  25. package/esm/methods/shortestPath.d.ts +2 -0
  26. package/esm/methods/shortestPath.d.ts.map +1 -1
  27. package/esm/methods/shortestPath.js +125 -20
  28. package/llm.md +278 -53
  29. package/llms.txt +1 -1
  30. package/package.json +1 -1
  31. package/script/class/SimpleTable.d.ts +249 -23
  32. package/script/class/SimpleTable.d.ts.map +1 -1
  33. package/script/class/SimpleTable.js +243 -23
  34. package/script/helpers/graphRouteResultSchema.d.ts +1 -1
  35. package/script/helpers/graphRouteResultSchema.d.ts.map +1 -1
  36. package/script/helpers/graphRouteResultSchema.js +3 -2
  37. package/script/helpers/graphRouteResultSelect.d.ts +1 -1
  38. package/script/helpers/graphRouteResultSelect.d.ts.map +1 -1
  39. package/script/helpers/graphRouteResultSelect.js +4 -1
  40. package/script/helpers/prepareGraphMetricOptions.d.ts +14 -0
  41. package/script/helpers/prepareGraphMetricOptions.d.ts.map +1 -0
  42. package/script/helpers/prepareGraphMetricOptions.js +43 -0
  43. package/script/helpers/prepareGraphTemporalSql.d.ts +3 -0
  44. package/script/helpers/prepareGraphTemporalSql.d.ts.map +1 -1
  45. package/script/helpers/prepareGraphTemporalSql.js +7 -0
  46. package/script/methods/distances.d.ts +2 -0
  47. package/script/methods/distances.d.ts.map +1 -1
  48. package/script/methods/distances.js +98 -6
  49. package/script/methods/findCycles.d.ts +1 -0
  50. package/script/methods/findCycles.d.ts.map +1 -1
  51. package/script/methods/findCycles.js +22 -15
  52. package/script/methods/paths.d.ts +1 -0
  53. package/script/methods/paths.d.ts.map +1 -1
  54. package/script/methods/paths.js +21 -14
  55. package/script/methods/shortestPath.d.ts +2 -0
  56. package/script/methods/shortestPath.d.ts.map +1 -1
  57. package/script/methods/shortestPath.js +125 -20
@@ -1,5 +1,6 @@
1
1
  import buildGraphTemporalCostStateSql from "../helpers/buildGraphTemporalCostStateSql.js";
2
2
  import getGraphWeightColumn from "../helpers/getGraphWeightColumn.js";
3
+ import prepareGraphMetricOptions from "../helpers/prepareGraphMetricOptions.js";
3
4
  import prepareGraphTemporalSql, { prepareGraphTemporalOptions, } from "../helpers/prepareGraphTemporalSql.js";
4
5
  import prepareGraphStarts from "../helpers/prepareGraphStarts.js";
5
6
  import prepareGraphTraversal, { validateGraphStarts, } from "../helpers/prepareGraphTraversal.js";
@@ -28,6 +29,7 @@ export default function distances(simpleTable, sourceColumn, targetColumn, start
28
29
  typeof options.outputTable !== "boolean") {
29
30
  throw new TypeError("distances() options.outputTable must be a string or boolean.");
30
31
  }
32
+ const metrics = prepareGraphMetricOptions(options, "distances()", true);
31
33
  const preparedStarts = prepareGraphStarts(startNodes, "distances()", "startNodes");
32
34
  const direction = options.direction ?? "outgoing";
33
35
  const temporalOptions = prepareGraphTemporalOptions(options, direction, "distances()");
@@ -51,13 +53,14 @@ export default function distances(simpleTable, sourceColumn, targetColumn, start
51
53
  ? preparedStarts.values
52
54
  : [temporal.gapParameter, ...preparedStarts.values];
53
55
  },
54
- buildSelect: (input, schema) => distancesSelect(input, schema, sourceColumn, targetColumn, preparedStarts, direction, options.weight, temporalOptions),
56
+ buildSelect: (input, schema) => distancesSelect(input, schema, sourceColumn, targetColumn, preparedStarts, direction, options.weight, temporalOptions, metrics),
55
57
  outputSchema: (schema) => {
56
58
  const validated = validateDistanceInputs(schema, sourceColumn, targetColumn, preparedStarts, options.weight, temporalOptions);
57
59
  return {
58
60
  start: validated.idType,
59
61
  node: validated.idType,
60
- distance: validated.distanceType,
62
+ total: validated.distanceType,
63
+ ...(metrics.elapsedTime ? { elapsedTimeMs: "DOUBLE" } : {}),
61
64
  };
62
65
  },
63
66
  });
@@ -72,7 +75,7 @@ function validateDistanceInputs(schema, source, target, starts, weight, temporal
72
75
  : prepareGraphTemporalSql(schema, temporalOptions, "distances()");
73
76
  return { idType: endpoints.idType, distanceType, temporal };
74
77
  }
75
- function distancesSelect(input, schema, source, target, starts, direction, weight, temporalOptions) {
78
+ function distancesSelect(input, schema, source, target, starts, direction, weight, temporalOptions, metrics) {
76
79
  const prepared = prepareGraphTraversal(input, schema, source, target, starts, "distances()", "startNodes");
77
80
  const weightColumn = weight === undefined
78
81
  ? undefined
@@ -83,6 +86,9 @@ function distancesSelect(input, schema, source, target, starts, direction, weigh
83
86
  : `CAST(${quoteIdentifier("edges")}.${quoteIdentifier(weightColumn.column)} AS ${distanceType})`;
84
87
  if (temporalOptions !== undefined) {
85
88
  const temporal = prepareGraphTemporalSql(schema, temporalOptions, "distances()");
89
+ if (metrics.elapsedTime) {
90
+ return elapsedDistancesSelect(prepared, direction, temporal, distanceType, edgeWeight, metrics.minimize);
91
+ }
86
92
  return temporalDistancesSelect(prepared, direction, temporal, distanceType, edgeWeight);
87
93
  }
88
94
  const relations = prepared.relationNames([
@@ -144,7 +150,7 @@ function distancesSelect(input, schema, source, target, starts, direction, weigh
144
150
  HAVING ${bestDistance} IS NULL OR MIN(${candidateDistance}) < ${bestDistance}
145
151
  )
146
152
  SELECT ${quoteIdentifier("start")}, ${quoteIdentifier("node")},
147
- ${quoteIdentifier("distance")}
153
+ ${quoteIdentifier("distance")} AS ${quoteIdentifier("total")}
148
154
  FROM ${distancesRelation}
149
155
  ORDER BY ${quoteIdentifier("__start_key")}, ${quoteIdentifier("distance")}, ${quoteIdentifier("__node_key")}`;
150
156
  }
@@ -156,9 +162,95 @@ function temporalDistancesSelect(prepared, direction, temporal, distanceType, ed
156
162
  const costStates = buildGraphTemporalCostStateSql(prepared, startsSelect, direction, temporal, distanceType, edgeWeight);
157
163
  return `${costStates.withClause}
158
164
  SELECT ${q("start")}, ${q("node")},
159
- MIN(${q("distance")}) AS ${q("distance")}
165
+ MIN(${q("distance")}) AS ${q("total")}
160
166
  FROM ${costStates.costRelation}
161
167
  GROUP BY ${q("start")}, ${q("node")}, ${q("__start_key")},
162
168
  ${q("__node_key")}
163
- ORDER BY ${q("__start_key")}, ${q("distance")}, ${q("__node_key")}`;
169
+ ORDER BY ${q("__start_key")}, ${q("total")}, ${q("__node_key")}`;
170
+ }
171
+ function elapsedDistancesSelect(prepared, direction, temporal, distanceType, edgeWeight, minimize) {
172
+ const q = quoteIdentifier;
173
+ const relations = prepared.relationNames([
174
+ "graph_starts",
175
+ "graph_temporal_settings",
176
+ "graph_event_rows",
177
+ "graph_edges",
178
+ "graph_elapsed_states",
179
+ "graph_elapsed_results",
180
+ ]);
181
+ const starts = relations.graph_starts;
182
+ const settings = relations.graph_temporal_settings;
183
+ const events = relations.graph_event_rows;
184
+ const edges = relations.graph_edges;
185
+ const states = relations.graph_elapsed_states;
186
+ const results = relations.graph_elapsed_results;
187
+ const ref = (alias, column) => `${q(alias)}.${q(column)}`;
188
+ const anchor = temporal.journeyAnchor("edges", direction);
189
+ const reachedAnchor = ref("reached", "__anchor");
190
+ const candidateDistance = `CAST(${ref("reached", "distance")} + ${ref("edges", "__weight")} AS ${distanceType})`;
191
+ const order = minimize === "elapsedTime"
192
+ ? `${q("__elapsed")}, ${q("distance")}`
193
+ : `${q("distance")}, ${q("__elapsed")}`;
194
+ // A physical event and the first departure (last arrival for incoming
195
+ // traversal) fix elapsed time. Keep the cheapest cost for each such state;
196
+ // merging different anchors would lose later-departing, faster journeys.
197
+ // The state space is finite, and strict improvements terminate zero cycles.
198
+ return `WITH RECURSIVE ${settings} AS MATERIALIZED (
199
+ SELECT CAST(? AS HUGEINT) AS ${q("__gap")}
200
+ ), ${events} AS MATERIALIZED (
201
+ ${prepared.edges(direction, [
202
+ `${edgeWeight} AS ${q("__weight")}`,
203
+ ...temporal.eventSelections("edges"),
204
+ ])}
205
+ ), ${edges} AS MATERIALIZED (
206
+ SELECT * FROM ${events} AS ${q("events")}
207
+ WHERE ${temporal.eventValidity("events")}
208
+ ), ${starts} AS MATERIALIZED (
209
+ SELECT ${q("start")}, ${prepared.key(q("start"))} AS ${q("__key")}
210
+ FROM (VALUES ${prepared.startValues}) AS ${q("start_values")}(${q("start")})
211
+ ), ${states}(
212
+ ${q("start")}, ${q("node")}, ${q("__start_key")}, ${q("__node_key")},
213
+ ${q("__event_id")}, ${q("__event_start")}, ${q("__event_end")},
214
+ ${q("__anchor")}, ${q("distance")}
215
+ ) USING KEY(${q("__start_key")}, ${q("__event_id")}, ${q("__anchor")}) AS (
216
+ SELECT ${ref("starts", "start")}, ${ref("edges", "__to")},
217
+ ${ref("starts", "__key")}, ${ref("edges", "__to_key")},
218
+ ${ref("edges", "__event_id")}, ${ref("edges", "__event_start")},
219
+ ${ref("edges", "__event_end")}, ${anchor}, ${ref("edges", "__weight")}
220
+ FROM ${starts} AS ${q("starts")}
221
+ INNER JOIN ${edges} AS ${q("edges")}
222
+ ON ${ref("starts", "__key")} = ${ref("edges", "__from_key")}
223
+ UNION
224
+ SELECT ${ref("reached", "start")}, ${ref("edges", "__to")},
225
+ ${ref("reached", "__start_key")}, ${ref("edges", "__to_key")},
226
+ ${ref("edges", "__event_id")}, ${ref("edges", "__event_start")},
227
+ ${ref("edges", "__event_end")}, ${reachedAnchor},
228
+ MIN(${candidateDistance}) AS ${q("distance")}
229
+ FROM ${states} AS ${q("reached")}
230
+ INNER JOIN ${edges} AS ${q("edges")}
231
+ ON ${ref("reached", "__node_key")} = ${ref("edges", "__from_key")}
232
+ CROSS JOIN ${settings} AS ${q("settings")}
233
+ LEFT JOIN recurring.${states} AS ${q("best")}
234
+ ON ${ref("reached", "__start_key")} = ${ref("best", "__start_key")}
235
+ AND ${ref("edges", "__event_id")} = ${ref("best", "__event_id")}
236
+ AND ${reachedAnchor} = ${ref("best", "__anchor")}
237
+ WHERE ${temporal.transition("reached", "edges", direction, ref("settings", "__gap"))}
238
+ GROUP BY ${ref("reached", "start")}, ${ref("edges", "__to")},
239
+ ${ref("reached", "__start_key")}, ${ref("edges", "__to_key")},
240
+ ${ref("edges", "__event_id")}, ${ref("edges", "__event_start")},
241
+ ${ref("edges", "__event_end")}, ${reachedAnchor}, ${ref("best", "distance")}
242
+ HAVING ${ref("best", "distance")} IS NULL OR
243
+ MIN(${candidateDistance}) < ${ref("best", "distance")}
244
+ ), ${results} AS (
245
+ SELECT *, ${temporal.journeyElapsed("states", ref("states", "__anchor"), direction)} AS ${q("__elapsed")}
246
+ FROM ${states} AS ${q("states")}
247
+ )
248
+ SELECT ${q("start")}, ${q("node")}, ${q("distance")} AS ${q("total")},
249
+ ${temporal.elapsedMilliseconds(q("__elapsed"))} AS ${q("elapsedTimeMs")}
250
+ FROM ${results}
251
+ QUALIFY row_number() OVER (
252
+ PARTITION BY ${q("__start_key")}, ${q("__node_key")}
253
+ ORDER BY ${order}, ${q("__event_id")}, ${q("__anchor")}
254
+ ) = 1
255
+ ORDER BY ${q("__start_key")}, ${order}, ${q("__node_key")}`;
164
256
  }
@@ -3,6 +3,7 @@ import { type GraphTemporalOptions } from "../helpers/prepareGraphTemporalSql.js
3
3
  import { type GraphId } from "../helpers/prepareGraphStarts.js";
4
4
  import { type GraphDirection } from "../helpers/prepareGraphTraversal.js";
5
5
  type FindCyclesOptions = GraphTemporalOptions & {
6
+ elapsedTime?: boolean;
6
7
  direction?: GraphDirection;
7
8
  outputTable?: string | boolean;
8
9
  weight?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"findCycles.d.ts","sourceRoot":"","sources":["../../src/methods/findCycles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAMvD,OAAgC,EAC9B,KAAK,oBAAoB,EAG1B,MAAM,uCAAuC,CAAC;AAC/C,OAA2B,EACzB,KAAK,OAAO,EAEb,MAAM,kCAAkC,CAAC;AAC1C,OAA8B,EAC5B,KAAK,cAAc,EAEpB,MAAM,qCAAqC,CAAC;AAM7C,KAAK,iBAAiB,GAAG,oBAAoB,GAAG;IAC9C,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,UAAU,CAChC,WAAW,EAAE,WAAW,EACxB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,OAAO,GAAG,OAAO,EAAE,EAC/B,OAAO,GAAE,iBAAsB,GAC9B,WAAW,CAuHb"}
1
+ {"version":3,"file":"findCycles.d.ts","sourceRoot":"","sources":["../../src/methods/findCycles.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAMvD,OAAgC,EAC9B,KAAK,oBAAoB,EAG1B,MAAM,uCAAuC,CAAC;AAC/C,OAA2B,EACzB,KAAK,OAAO,EAEb,MAAM,kCAAkC,CAAC;AAC1C,OAA8B,EAC5B,KAAK,cAAc,EAEpB,MAAM,qCAAqC,CAAC;AAM7C,KAAK,iBAAiB,GAAG,oBAAoB,GAAG;IAC9C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,UAAU,CAChC,WAAW,EAAE,WAAW,EACxB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,OAAO,GAAG,OAAO,EAAE,EAC/B,OAAO,GAAE,iBAAsB,GAC9B,WAAW,CAgIb"}
@@ -1,3 +1,4 @@
1
+ import prepareGraphMetricOptions from "../helpers/prepareGraphMetricOptions.js";
1
2
  import getGraphEdgeIdColumn from "../helpers/getGraphEdgeIdColumn.js";
2
3
  import getGraphWeightColumn from "../helpers/getGraphWeightColumn.js";
3
4
  import graphRouteResultSchema from "../helpers/graphRouteResultSchema.js";
@@ -35,6 +36,7 @@ export default function findCycles(simpleTable, sourceColumn, targetColumn, edge
35
36
  typeof options.outputTable !== "boolean") {
36
37
  throw new TypeError("findCycles() options.outputTable must be a string or boolean.");
37
38
  }
39
+ const metric = prepareGraphMetricOptions(options, "findCycles()", false);
38
40
  const direction = options.direction ?? "outgoing";
39
41
  const temporalOptions = prepareGraphTemporalOptions(options, direction, "findCycles()");
40
42
  options = structuredClone(options);
@@ -53,7 +55,8 @@ export default function findCycles(simpleTable, sourceColumn, targetColumn, edge
53
55
  ? undefined
54
56
  : (input) => validateGraphTemporalEvents(input, temporalOptions, "findCycles()", parameters),
55
57
  values: (schema) => {
56
- validateFindCyclesInputs(schema, sourceColumn, targetColumn, edgeId, preparedStarts, options.weight);
58
+ const validated = validateFindCyclesInputs(schema, sourceColumn, targetColumn, edgeId, preparedStarts, options.weight);
59
+ graphRouteResultSchema(schema, validated.distanceType, "findCycles()", metric.elapsedTime);
57
60
  const temporal = temporalOptions === undefined
58
61
  ? undefined
59
62
  : prepareGraphTemporalSql(schema, temporalOptions, "findCycles()");
@@ -61,7 +64,7 @@ export default function findCycles(simpleTable, sourceColumn, targetColumn, edge
61
64
  ? preparedStarts.values
62
65
  : [temporal.gapParameter, ...preparedStarts.values];
63
66
  },
64
- buildSelect: (input, schema) => findCyclesSelect(input, schema, sourceColumn, targetColumn, edgeId, preparedStarts, direction, options.weight, temporalOptions),
67
+ buildSelect: (input, schema) => findCyclesSelect(input, schema, sourceColumn, targetColumn, edgeId, preparedStarts, direction, options.weight, temporalOptions, metric.elapsedTime),
65
68
  outputSchema: (schema) => {
66
69
  const validated = validateFindCyclesInputs(schema, sourceColumn, targetColumn, edgeId, preparedStarts, options.weight);
67
70
  if (temporalOptions !== undefined) {
@@ -69,7 +72,7 @@ export default function findCycles(simpleTable, sourceColumn, targetColumn, edge
69
72
  }
70
73
  return {
71
74
  start: validated.nodeIdType,
72
- ...graphRouteResultSchema(schema, validated.distanceType, "findCycles()"),
75
+ ...graphRouteResultSchema(schema, validated.distanceType, "findCycles()", metric.elapsedTime),
73
76
  };
74
77
  },
75
78
  });
@@ -87,7 +90,7 @@ function validateFindCyclesInputs(schema, source, target, edgeId, starts, weight
87
90
  nodeIdType: endpoints.idType,
88
91
  };
89
92
  }
90
- function findCyclesSelect(input, schema, source, target, edgeId, starts, direction, weight, temporalOptions) {
93
+ function findCyclesSelect(input, schema, source, target, edgeId, starts, direction, weight, temporalOptions, elapsedTime) {
91
94
  const prepared = prepareGraphTraversal(input, schema, source, target, starts, "findCycles()", "startNodes");
92
95
  const edgeIdColumn = getGraphEdgeIdColumn(schema, edgeId, "findCycles()");
93
96
  const weightColumn = weight === undefined
@@ -103,10 +106,10 @@ function findCyclesSelect(input, schema, source, target, edgeId, starts, directi
103
106
  const edgeWeight = weightColumn === undefined
104
107
  ? `CAST(1 AS ${distanceType})`
105
108
  : `CAST(${q("edges")}.${q(weightColumn.column)} AS ${distanceType})`;
106
- const stepType = `STRUCT(${q("edgeId")} ${edgeIdColumn.idType}, ${q("source")} ${prepared.endpoints.idType}, ${q("target")} ${prepared.endpoints.idType}, ${q("weight")} ${distanceType}, ${q("distance")} ${distanceType})`;
109
+ const stepType = `STRUCT(${q("edgeId")} ${edgeIdColumn.idType}, ${q("source")} ${prepared.endpoints.idType}, ${q("target")} ${prepared.endpoints.idType}, ${q("weight")} ${distanceType}, ${q("distance")} ${distanceType}${elapsedTime ? `, ${q("elapsedTimeMs")} DOUBLE` : ""})`;
107
110
  if (temporalOptions !== undefined) {
108
111
  const temporal = prepareGraphTemporalSql(schema, temporalOptions, "findCycles()");
109
- return temporalFindCyclesSelect(prepared, direction, temporal, typedEdgeId, edgeKey, edgeWeight, distanceType, stepType, input, edgeIdColumn);
112
+ return temporalFindCyclesSelect(prepared, direction, temporal, typedEdgeId, edgeKey, edgeWeight, distanceType, stepType, input, edgeIdColumn, elapsedTime);
110
113
  }
111
114
  const relations = prepared.relationNames([
112
115
  "graph_start_values",
@@ -210,7 +213,7 @@ function findCyclesSelect(input, schema, source, target, edgeId, starts, directi
210
213
  )
211
214
  ${graphRouteResultSelect(rankedRelation, input, edgeIdColumn, true)}`;
212
215
  }
213
- function temporalFindCyclesSelect(prepared, direction, temporal, typedEdgeId, edgeKey, edgeWeight, distanceType, stepType, input, edgeIdColumn) {
216
+ function temporalFindCyclesSelect(prepared, direction, temporal, typedEdgeId, edgeKey, edgeWeight, distanceType, stepType, input, edgeIdColumn, elapsedTime) {
214
217
  const q = quoteIdentifier;
215
218
  const relations = prepared.relationNames([
216
219
  "graph_start_values",
@@ -240,12 +243,16 @@ function temporalFindCyclesSelect(prepared, direction, temporal, typedEdgeId, ed
240
243
  const walkDistance = `${q("walks")}.${q("distance")}`;
241
244
  const candidateDistance = `CAST(${walkDistance} + ${edgeCost} AS ${distanceType})`;
242
245
  const gap = `${q("settings")}.${q("__gap")}`;
243
- const step = (distance) => `struct_pack(
246
+ const firstAnchor = temporal.journeyAnchor("edges", direction);
247
+ const anchor = `${q("walks")}.${q("__journey_anchor")}`;
248
+ const step = (distance, journeyAnchor) => `struct_pack(
244
249
  ${q("edgeId")} := ${edgeIdentity},
245
250
  ${q("source")} := ${edgeFrom},
246
251
  ${q("target")} := ${edgeTo},
247
252
  ${q("weight")} := ${edgeCost},
248
- ${q("distance")} := ${distance}
253
+ ${q("distance")} := ${distance}${elapsedTime
254
+ ? `, ${q("elapsedTimeMs")} := ${temporal.elapsedMilliseconds(temporal.journeyElapsed("edges", journeyAnchor, direction))}`
255
+ : ""}
249
256
  )`;
250
257
  return `WITH RECURSIVE ${settingsRelation} AS MATERIALIZED (
251
258
  SELECT CAST(? AS HUGEINT) AS ${q("__gap")}
@@ -271,7 +278,7 @@ function temporalFindCyclesSelect(prepared, direction, temporal, typedEdgeId, ed
271
278
  ${q("__node_key")}, ${q("__visited")}, ${q("__event_ids")},
272
279
  ${q("__edge_keys")}, ${q("steps")}, ${q("distance")},
273
280
  ${q("closed")}, ${q("__event_id")}, ${q("__event_start")},
274
- ${q("__event_end")}
281
+ ${q("__event_end")}${elapsedTime ? `, ${q("__journey_anchor")}` : ""}
275
282
  ) AS (
276
283
  SELECT ${q("starts")}.${q("start")},
277
284
  ${q("starts")}.${q("__start_key")}, ${edgeTo}, ${edgeToKey},
@@ -280,11 +287,11 @@ function temporalFindCyclesSelect(prepared, direction, temporal, typedEdgeId, ed
280
287
  ELSE [${edgeFromKey}, ${edgeToKey}]
281
288
  END,
282
289
  [${q("edges")}.${q("__event_id")}], [${edgeIdentityKey}],
283
- [${step(edgeCost)}]::${stepType}[], ${edgeCost},
290
+ [${step(edgeCost, firstAnchor)}]::${stepType}[], ${edgeCost},
284
291
  ${edgeFromKey} = ${edgeToKey},
285
292
  ${q("edges")}.${q("__event_id")},
286
293
  ${q("edges")}.${q("__event_start")},
287
- ${q("edges")}.${q("__event_end")}
294
+ ${q("edges")}.${q("__event_end")}${elapsedTime ? `, ${firstAnchor}` : ""}
288
295
  FROM ${edgesRelation} AS ${q("edges")}
289
296
  INNER JOIN ${startsRelation} AS ${q("starts")}
290
297
  ON ${q("starts")}.${q("__start_key")} = ${edgeFromKey}
@@ -295,12 +302,12 @@ function temporalFindCyclesSelect(prepared, direction, temporal, typedEdgeId, ed
295
302
  list_append(${q("walks")}.${q("__event_ids")},
296
303
  ${q("edges")}.${q("__event_id")}),
297
304
  list_append(${q("walks")}.${q("__edge_keys")}, ${edgeIdentityKey}),
298
- list_append(${q("walks")}.${q("steps")}, ${step(candidateDistance)}),
305
+ list_append(${q("walks")}.${q("steps")}, ${step(candidateDistance, anchor)}),
299
306
  ${candidateDistance},
300
307
  ${edgeToKey} = ${q("walks")}.${q("__start_key")},
301
308
  ${q("edges")}.${q("__event_id")},
302
309
  ${q("edges")}.${q("__event_start")},
303
- ${q("edges")}.${q("__event_end")}
310
+ ${q("edges")}.${q("__event_end")}${elapsedTime ? `, ${anchor}` : ""}
304
311
  FROM ${walksRelation} AS ${q("walks")}
305
312
  INNER JOIN ${edgesRelation} AS ${q("edges")}
306
313
  ON ${q("walks")}.${q("__node_key")} = ${edgeFromKey}
@@ -326,5 +333,5 @@ function temporalFindCyclesSelect(prepared, direction, temporal, typedEdgeId, ed
326
333
  AS BIGINT) AS ${q("pathId")}, ${q("steps")}
327
334
  FROM ${completeRelation}
328
335
  )
329
- ${graphRouteResultSelect(rankedRelation, input, edgeIdColumn, true)}`;
336
+ ${graphRouteResultSelect(rankedRelation, input, edgeIdColumn, true, elapsedTime)}`;
330
337
  }
@@ -3,6 +3,7 @@ import type { GraphId } from "../helpers/prepareGraphStarts.js";
3
3
  import { type GraphTemporalOptions } from "../helpers/prepareGraphTemporalSql.js";
4
4
  import type { GraphDirection } from "../helpers/prepareGraphTraversal.js";
5
5
  type PathsOptions = GraphTemporalOptions & {
6
+ elapsedTime?: boolean;
6
7
  direction?: GraphDirection;
7
8
  outputTable?: string | boolean;
8
9
  weight?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/methods/paths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AASvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAgC,EAC9B,KAAK,oBAAoB,EAG1B,MAAM,uCAAuC,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AAM1E,KAAK,YAAY,GAAG,oBAAoB,GAAG;IACzC,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,KAAK,CAC3B,WAAW,EAAE,WAAW,EACxB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,OAAO,EACZ,OAAO,GAAE,YAAiB,GACzB,WAAW,CA+Gb"}
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/methods/paths.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AASvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAgC,EAC9B,KAAK,oBAAoB,EAG1B,MAAM,uCAAuC,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AAM1E,KAAK,YAAY,GAAG,oBAAoB,GAAG;IACzC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,KAAK,CAC3B,WAAW,EAAE,WAAW,EACxB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,OAAO,EACZ,OAAO,GAAE,YAAiB,GACzB,WAAW,CA4Hb"}
@@ -1,3 +1,4 @@
1
+ import prepareGraphMetricOptions from "../helpers/prepareGraphMetricOptions.js";
1
2
  import { prepareGraphRouteEndpoints, prepareGraphRouteSql, validateGraphRouteInputs, } from "../helpers/prepareGraphRouteSql.js";
2
3
  import graphRouteResultSchema from "../helpers/graphRouteResultSchema.js";
3
4
  import graphRouteResultSelect from "../helpers/graphRouteResultSelect.js";
@@ -30,6 +31,7 @@ export default function paths(simpleTable, sourceColumn, targetColumn, edgeId, s
30
31
  typeof options.outputTable !== "boolean") {
31
32
  throw new TypeError("paths() options.outputTable must be a string or boolean.");
32
33
  }
34
+ const metric = prepareGraphMetricOptions(options, "paths()", false);
33
35
  const endpoints = prepareGraphRouteEndpoints(start, end, "paths()");
34
36
  const direction = options.direction ?? "outgoing";
35
37
  const temporalOptions = prepareGraphTemporalOptions(options, direction, "paths()");
@@ -50,7 +52,8 @@ export default function paths(simpleTable, sourceColumn, targetColumn, edgeId, s
50
52
  ? undefined
51
53
  : (input) => validateGraphTemporalEvents(input, temporalOptions, "paths()", parameters),
52
54
  values: (schema) => {
53
- validateGraphRouteInputs(schema, sourceColumn, targetColumn, edgeId, endpoints, options.weight, "paths()");
55
+ const validated = validateGraphRouteInputs(schema, sourceColumn, targetColumn, edgeId, endpoints, options.weight, "paths()");
56
+ graphRouteResultSchema(schema, validated.distanceType, "paths()", metric.elapsedTime);
54
57
  const temporal = temporalOptions === undefined
55
58
  ? undefined
56
59
  : prepareGraphTemporalSql(schema, temporalOptions, "paths()");
@@ -58,22 +61,22 @@ export default function paths(simpleTable, sourceColumn, targetColumn, edgeId, s
58
61
  ? endpoints.values.values
59
62
  : [temporal.gapParameter, ...endpoints.values.values];
60
63
  },
61
- buildSelect: (input, schema) => pathsSelect(input, schema, sourceColumn, targetColumn, edgeId, endpoints, direction, options.weight, temporalOptions),
64
+ buildSelect: (input, schema) => pathsSelect(input, schema, sourceColumn, targetColumn, edgeId, endpoints, direction, options.weight, temporalOptions, metric.elapsedTime),
62
65
  outputSchema: (schema) => {
63
66
  const validated = validateGraphRouteInputs(schema, sourceColumn, targetColumn, edgeId, endpoints, options.weight, "paths()");
64
67
  if (temporalOptions !== undefined) {
65
68
  prepareGraphTemporalSql(schema, temporalOptions, "paths()");
66
69
  }
67
- return graphRouteResultSchema(schema, validated.distanceType, "paths()");
70
+ return graphRouteResultSchema(schema, validated.distanceType, "paths()", metric.elapsedTime);
68
71
  },
69
72
  });
70
73
  }
71
- function pathsSelect(input, schema, source, target, edgeId, routeEndpoints, direction, weight, temporalOptions) {
74
+ function pathsSelect(input, schema, source, target, edgeId, routeEndpoints, direction, weight, temporalOptions, elapsedTime) {
72
75
  const route = prepareGraphRouteSql(input, schema, source, target, edgeId, routeEndpoints, weight, "paths()");
73
76
  const prepared = route.traversal;
74
77
  if (temporalOptions !== undefined) {
75
78
  const temporal = prepareGraphTemporalSql(schema, temporalOptions, "paths()");
76
- return temporalPathsSelect(route, direction, temporal);
79
+ return temporalPathsSelect(route, direction, temporal, elapsedTime);
77
80
  }
78
81
  const distanceType = route.distanceType;
79
82
  const q = quoteIdentifier;
@@ -166,7 +169,7 @@ function pathsSelect(input, schema, source, target, edgeId, routeEndpoints, dire
166
169
  )
167
170
  ${graphRouteResultSelect(rankedRelation, route.input, route.edgeIdColumn)}`;
168
171
  }
169
- function temporalPathsSelect(route, direction, temporal) {
172
+ function temporalPathsSelect(route, direction, temporal, elapsedTime) {
170
173
  const prepared = route.traversal;
171
174
  const distanceType = route.distanceType;
172
175
  const q = quoteIdentifier;
@@ -197,12 +200,16 @@ function temporalPathsSelect(route, direction, temporal) {
197
200
  const edgeKey = `${q("edges")}.${q("__edge_key")}`;
198
201
  const candidateDistance = `CAST(${q("routes")}.${q("distance")} + ${edgeCost} AS ${distanceType})`;
199
202
  const gap = `${q("settings")}.${q("__gap")}`;
200
- const step = (distance) => `struct_pack(
203
+ const firstAnchor = temporal.journeyAnchor("edges", direction);
204
+ const anchor = `${q("routes")}.${q("__journey_anchor")}`;
205
+ const step = (distance, journeyAnchor) => `struct_pack(
201
206
  ${q("edgeId")} := ${q("edges")}.${q("__edge_id")},
202
207
  ${q("source")} := ${q("edges")}.${q("__from")},
203
208
  ${q("target")} := ${edgeTo},
204
209
  ${q("weight")} := ${edgeCost},
205
- ${q("distance")} := ${distance}
210
+ ${q("distance")} := ${distance}${elapsedTime
211
+ ? `, ${q("elapsedTimeMs")} := ${temporal.elapsedMilliseconds(temporal.journeyElapsed("edges", journeyAnchor, direction))}`
212
+ : ""}
206
213
  )`;
207
214
  return `WITH RECURSIVE ${settingsRelation} AS MATERIALIZED (
208
215
  SELECT CAST(? AS HUGEINT) AS ${q("__gap")}
@@ -241,14 +248,14 @@ function temporalPathsSelect(route, direction, temporal) {
241
248
  ), ${routesRelation}(
242
249
  ${q("node")}, ${q("__node_key")}, ${q("__visited")},
243
250
  ${q("__edge_keys")}, ${q("steps")}, ${q("distance")},
244
- ${q("__event_id")}, ${q("__event_start")}, ${q("__event_end")}
251
+ ${q("__event_id")}, ${q("__event_start")}, ${q("__event_end")}${elapsedTime ? `, ${q("__journey_anchor")}` : ""}
245
252
  ) AS (
246
253
  SELECT ${edgeTo}, ${edgeToKey},
247
254
  [${q("__start_key")}, ${edgeToKey}], [${edgeKey}],
248
- [${step(edgeCost)}], ${edgeCost},
255
+ [${step(edgeCost, firstAnchor)}], ${edgeCost},
249
256
  ${q("edges")}.${q("__event_id")},
250
257
  ${q("edges")}.${q("__event_start")},
251
- ${q("edges")}.${q("__event_end")}
258
+ ${q("edges")}.${q("__event_end")}${elapsedTime ? `, ${firstAnchor}` : ""}
252
259
  FROM ${endpointRelation}
253
260
  INNER JOIN ${edgesRelation} AS ${q("edges")}
254
261
  ON ${q("__start_key")} = ${edgeFromKey}
@@ -259,10 +266,10 @@ function temporalPathsSelect(route, direction, temporal) {
259
266
  SELECT ${edgeTo}, ${edgeToKey},
260
267
  list_append(${q("routes")}.${q("__visited")}, ${edgeToKey}),
261
268
  list_append(${q("routes")}.${q("__edge_keys")}, ${edgeKey}),
262
- list_append(${q("routes")}.${q("steps")}, ${step(candidateDistance)}),
269
+ list_append(${q("routes")}.${q("steps")}, ${step(candidateDistance, anchor)}),
263
270
  ${candidateDistance}, ${q("edges")}.${q("__event_id")},
264
271
  ${q("edges")}.${q("__event_start")},
265
- ${q("edges")}.${q("__event_end")}
272
+ ${q("edges")}.${q("__event_end")}${elapsedTime ? `, ${anchor}` : ""}
266
273
  FROM ${routesRelation} AS ${q("routes")}
267
274
  INNER JOIN ${edgesRelation} AS ${q("edges")}
268
275
  ON ${q("routes")}.${q("__node_key")} = ${edgeFromKey}
@@ -283,5 +290,5 @@ function temporalPathsSelect(route, direction, temporal) {
283
290
  AS ${q("pathId")}, ${q("steps")}
284
291
  FROM ${completeRelation}
285
292
  )
286
- ${graphRouteResultSelect(rankedRelation, route.input, route.edgeIdColumn)}`;
293
+ ${graphRouteResultSelect(rankedRelation, route.input, route.edgeIdColumn, false, elapsedTime)}`;
287
294
  }
@@ -6,6 +6,8 @@ type ShortestPathOptions = GraphTemporalOptions & {
6
6
  direction?: GraphDirection;
7
7
  outputTable?: string | boolean;
8
8
  weight?: string;
9
+ elapsedTime?: boolean;
10
+ minimize?: "weight" | "elapsedTime";
9
11
  };
10
12
  export default function shortestPath(simpleTable: SimpleTable, sourceColumn: string, targetColumn: string, edgeId: string, start: GraphId, end: GraphId, options?: ShortestPathOptions): SimpleTable;
11
13
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"shortestPath.d.ts","sourceRoot":"","sources":["../../src/methods/shortestPath.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAGvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAShE,OAAgC,EAC9B,KAAK,oBAAoB,EAG1B,MAAM,uCAAuC,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AAK1E,KAAK,mBAAmB,GAAG,oBAAoB,GAAG;IAChD,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,YAAY,CAClC,WAAW,EAAE,WAAW,EACxB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,OAAO,EACZ,OAAO,GAAE,mBAAwB,GAChC,WAAW,CAmHb"}
1
+ {"version":3,"file":"shortestPath.d.ts","sourceRoot":"","sources":["../../src/methods/shortestPath.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAMvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAShE,OAAgC,EAC9B,KAAK,oBAAoB,EAG1B,MAAM,uCAAuC,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AAK1E,KAAK,mBAAmB,GAAG,oBAAoB,GAAG;IAChD,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,QAAQ,GAAG,aAAa,CAAC;CACrC,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,YAAY,CAClC,WAAW,EAAE,WAAW,EACxB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,OAAO,EACZ,OAAO,GAAE,mBAAwB,GAChC,WAAW,CAsHb"}