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

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 (75) hide show
  1. package/esm/class/SimpleTable.d.ts +399 -90
  2. package/esm/class/SimpleTable.d.ts.map +1 -1
  3. package/esm/class/SimpleTable.js +393 -90
  4. package/esm/helpers/buildGraphTemporalCostStateSql.d.ts +1 -1
  5. package/esm/helpers/buildGraphTemporalCostStateSql.d.ts.map +1 -1
  6. package/esm/helpers/buildGraphTemporalCostStateSql.js +23 -9
  7. package/esm/helpers/graphRouteResultSchema.d.ts +1 -1
  8. package/esm/helpers/graphRouteResultSchema.d.ts.map +1 -1
  9. package/esm/helpers/graphRouteResultSchema.js +4 -3
  10. package/esm/helpers/graphRouteResultSelect.d.ts +1 -1
  11. package/esm/helpers/graphRouteResultSelect.d.ts.map +1 -1
  12. package/esm/helpers/graphRouteResultSelect.js +5 -2
  13. package/esm/helpers/prepareGraphMetricOptions.d.ts +14 -0
  14. package/esm/helpers/prepareGraphMetricOptions.d.ts.map +1 -0
  15. package/esm/helpers/prepareGraphMetricOptions.js +41 -0
  16. package/esm/helpers/prepareGraphRouteSql.d.ts +2 -2
  17. package/esm/helpers/prepareGraphRouteSql.d.ts.map +1 -1
  18. package/esm/helpers/prepareGraphRouteSql.js +4 -4
  19. package/esm/helpers/prepareGraphTemporalSql.d.ts +3 -0
  20. package/esm/helpers/prepareGraphTemporalSql.d.ts.map +1 -1
  21. package/esm/helpers/prepareGraphTemporalSql.js +7 -0
  22. package/esm/helpers/validateGraphRouteResultSchema.d.ts +1 -1
  23. package/esm/helpers/validateGraphRouteResultSchema.d.ts.map +1 -1
  24. package/esm/helpers/validateGraphRouteResultSchema.js +3 -1
  25. package/esm/methods/distances.d.ts +2 -0
  26. package/esm/methods/distances.d.ts.map +1 -1
  27. package/esm/methods/distances.js +274 -20
  28. package/esm/methods/findCycles.d.ts +1 -0
  29. package/esm/methods/findCycles.d.ts.map +1 -1
  30. package/esm/methods/findCycles.js +22 -15
  31. package/esm/methods/paths.d.ts +1 -0
  32. package/esm/methods/paths.d.ts.map +1 -1
  33. package/esm/methods/paths.js +21 -14
  34. package/esm/methods/shortestPath.d.ts +2 -0
  35. package/esm/methods/shortestPath.d.ts.map +1 -1
  36. package/esm/methods/shortestPath.js +155 -34
  37. package/llm.md +428 -108
  38. package/llms.txt +1 -1
  39. package/package.json +1 -1
  40. package/script/class/SimpleTable.d.ts +399 -90
  41. package/script/class/SimpleTable.d.ts.map +1 -1
  42. package/script/class/SimpleTable.js +393 -90
  43. package/script/helpers/buildGraphTemporalCostStateSql.d.ts +1 -1
  44. package/script/helpers/buildGraphTemporalCostStateSql.d.ts.map +1 -1
  45. package/script/helpers/buildGraphTemporalCostStateSql.js +23 -9
  46. package/script/helpers/graphRouteResultSchema.d.ts +1 -1
  47. package/script/helpers/graphRouteResultSchema.d.ts.map +1 -1
  48. package/script/helpers/graphRouteResultSchema.js +4 -3
  49. package/script/helpers/graphRouteResultSelect.d.ts +1 -1
  50. package/script/helpers/graphRouteResultSelect.d.ts.map +1 -1
  51. package/script/helpers/graphRouteResultSelect.js +5 -2
  52. package/script/helpers/prepareGraphMetricOptions.d.ts +14 -0
  53. package/script/helpers/prepareGraphMetricOptions.d.ts.map +1 -0
  54. package/script/helpers/prepareGraphMetricOptions.js +44 -0
  55. package/script/helpers/prepareGraphRouteSql.d.ts +2 -2
  56. package/script/helpers/prepareGraphRouteSql.d.ts.map +1 -1
  57. package/script/helpers/prepareGraphRouteSql.js +4 -4
  58. package/script/helpers/prepareGraphTemporalSql.d.ts +3 -0
  59. package/script/helpers/prepareGraphTemporalSql.d.ts.map +1 -1
  60. package/script/helpers/prepareGraphTemporalSql.js +7 -0
  61. package/script/helpers/validateGraphRouteResultSchema.d.ts +1 -1
  62. package/script/helpers/validateGraphRouteResultSchema.d.ts.map +1 -1
  63. package/script/helpers/validateGraphRouteResultSchema.js +3 -1
  64. package/script/methods/distances.d.ts +2 -0
  65. package/script/methods/distances.d.ts.map +1 -1
  66. package/script/methods/distances.js +274 -20
  67. package/script/methods/findCycles.d.ts +1 -0
  68. package/script/methods/findCycles.d.ts.map +1 -1
  69. package/script/methods/findCycles.js +22 -15
  70. package/script/methods/paths.d.ts +1 -0
  71. package/script/methods/paths.d.ts.map +1 -1
  72. package/script/methods/paths.js +21 -14
  73. package/script/methods/shortestPath.d.ts +2 -0
  74. package/script/methods/shortestPath.d.ts.map +1 -1
  75. package/script/methods/shortestPath.js +155 -34
@@ -37,6 +37,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.default = paths;
40
+ const prepareGraphMetricOptions_js_1 = __importDefault(require("../helpers/prepareGraphMetricOptions.js"));
40
41
  const prepareGraphRouteSql_js_1 = require("../helpers/prepareGraphRouteSql.js");
41
42
  const graphRouteResultSchema_js_1 = __importDefault(require("../helpers/graphRouteResultSchema.js"));
42
43
  const graphRouteResultSelect_js_1 = __importDefault(require("../helpers/graphRouteResultSelect.js"));
@@ -69,6 +70,7 @@ function paths(simpleTable, sourceColumn, targetColumn, edgeId, start, end, opti
69
70
  typeof options.outputTable !== "boolean") {
70
71
  throw new TypeError("paths() options.outputTable must be a string or boolean.");
71
72
  }
73
+ const metric = (0, prepareGraphMetricOptions_js_1.default)(options, "paths()", false);
72
74
  const endpoints = (0, prepareGraphRouteSql_js_1.prepareGraphRouteEndpoints)(start, end, "paths()");
73
75
  const direction = options.direction ?? "outgoing";
74
76
  const temporalOptions = (0, prepareGraphTemporalSql_js_1.prepareGraphTemporalOptions)(options, direction, "paths()");
@@ -89,7 +91,8 @@ function paths(simpleTable, sourceColumn, targetColumn, edgeId, start, end, opti
89
91
  ? undefined
90
92
  : (input) => (0, validateGraphTemporalEvents_js_1.default)(input, temporalOptions, "paths()", parameters),
91
93
  values: (schema) => {
92
- (0, prepareGraphRouteSql_js_1.validateGraphRouteInputs)(schema, sourceColumn, targetColumn, edgeId, endpoints, options.weight, "paths()");
94
+ const validated = (0, prepareGraphRouteSql_js_1.validateGraphRouteInputs)(schema, sourceColumn, targetColumn, edgeId, endpoints, options.weight, "paths()");
95
+ (0, graphRouteResultSchema_js_1.default)(schema, validated.distanceType, "paths()", metric.elapsedTime);
93
96
  const temporal = temporalOptions === undefined
94
97
  ? undefined
95
98
  : (0, prepareGraphTemporalSql_js_1.default)(schema, temporalOptions, "paths()");
@@ -97,22 +100,22 @@ function paths(simpleTable, sourceColumn, targetColumn, edgeId, start, end, opti
97
100
  ? endpoints.values.values
98
101
  : [temporal.gapParameter, ...endpoints.values.values];
99
102
  },
100
- buildSelect: (input, schema) => pathsSelect(input, schema, sourceColumn, targetColumn, edgeId, endpoints, direction, options.weight, temporalOptions),
103
+ buildSelect: (input, schema) => pathsSelect(input, schema, sourceColumn, targetColumn, edgeId, endpoints, direction, options.weight, temporalOptions, metric.elapsedTime),
101
104
  outputSchema: (schema) => {
102
105
  const validated = (0, prepareGraphRouteSql_js_1.validateGraphRouteInputs)(schema, sourceColumn, targetColumn, edgeId, endpoints, options.weight, "paths()");
103
106
  if (temporalOptions !== undefined) {
104
107
  (0, prepareGraphTemporalSql_js_1.default)(schema, temporalOptions, "paths()");
105
108
  }
106
- return (0, graphRouteResultSchema_js_1.default)(schema, validated.distanceType, "paths()");
109
+ return (0, graphRouteResultSchema_js_1.default)(schema, validated.distanceType, "paths()", metric.elapsedTime);
107
110
  },
108
111
  });
109
112
  }
110
- function pathsSelect(input, schema, source, target, edgeId, routeEndpoints, direction, weight, temporalOptions) {
113
+ function pathsSelect(input, schema, source, target, edgeId, routeEndpoints, direction, weight, temporalOptions, elapsedTime) {
111
114
  const route = (0, prepareGraphRouteSql_js_1.prepareGraphRouteSql)(input, schema, source, target, edgeId, routeEndpoints, weight, "paths()");
112
115
  const prepared = route.traversal;
113
116
  if (temporalOptions !== undefined) {
114
117
  const temporal = (0, prepareGraphTemporalSql_js_1.default)(schema, temporalOptions, "paths()");
115
- return temporalPathsSelect(route, direction, temporal);
118
+ return temporalPathsSelect(route, direction, temporal, elapsedTime);
116
119
  }
117
120
  const distanceType = route.distanceType;
118
121
  const q = quoteIdentifier_js_1.default;
@@ -205,7 +208,7 @@ function pathsSelect(input, schema, source, target, edgeId, routeEndpoints, dire
205
208
  )
206
209
  ${(0, graphRouteResultSelect_js_1.default)(rankedRelation, route.input, route.edgeIdColumn)}`;
207
210
  }
208
- function temporalPathsSelect(route, direction, temporal) {
211
+ function temporalPathsSelect(route, direction, temporal, elapsedTime) {
209
212
  const prepared = route.traversal;
210
213
  const distanceType = route.distanceType;
211
214
  const q = quoteIdentifier_js_1.default;
@@ -236,12 +239,16 @@ function temporalPathsSelect(route, direction, temporal) {
236
239
  const edgeKey = `${q("edges")}.${q("__edge_key")}`;
237
240
  const candidateDistance = `CAST(${q("routes")}.${q("distance")} + ${edgeCost} AS ${distanceType})`;
238
241
  const gap = `${q("settings")}.${q("__gap")}`;
239
- const step = (distance) => `struct_pack(
242
+ const firstAnchor = temporal.journeyAnchor("edges", direction);
243
+ const anchor = `${q("routes")}.${q("__journey_anchor")}`;
244
+ const step = (distance, journeyAnchor) => `struct_pack(
240
245
  ${q("edgeId")} := ${q("edges")}.${q("__edge_id")},
241
246
  ${q("source")} := ${q("edges")}.${q("__from")},
242
247
  ${q("target")} := ${edgeTo},
243
248
  ${q("weight")} := ${edgeCost},
244
- ${q("distance")} := ${distance}
249
+ ${q("distance")} := ${distance}${elapsedTime
250
+ ? `, ${q("elapsedTimeMs")} := ${temporal.elapsedMilliseconds(temporal.journeyElapsed("edges", journeyAnchor, direction))}`
251
+ : ""}
245
252
  )`;
246
253
  return `WITH RECURSIVE ${settingsRelation} AS MATERIALIZED (
247
254
  SELECT CAST(? AS HUGEINT) AS ${q("__gap")}
@@ -280,14 +287,14 @@ function temporalPathsSelect(route, direction, temporal) {
280
287
  ), ${routesRelation}(
281
288
  ${q("node")}, ${q("__node_key")}, ${q("__visited")},
282
289
  ${q("__edge_keys")}, ${q("steps")}, ${q("distance")},
283
- ${q("__event_id")}, ${q("__event_start")}, ${q("__event_end")}
290
+ ${q("__event_id")}, ${q("__event_start")}, ${q("__event_end")}${elapsedTime ? `, ${q("__journey_anchor")}` : ""}
284
291
  ) AS (
285
292
  SELECT ${edgeTo}, ${edgeToKey},
286
293
  [${q("__start_key")}, ${edgeToKey}], [${edgeKey}],
287
- [${step(edgeCost)}], ${edgeCost},
294
+ [${step(edgeCost, firstAnchor)}], ${edgeCost},
288
295
  ${q("edges")}.${q("__event_id")},
289
296
  ${q("edges")}.${q("__event_start")},
290
- ${q("edges")}.${q("__event_end")}
297
+ ${q("edges")}.${q("__event_end")}${elapsedTime ? `, ${firstAnchor}` : ""}
291
298
  FROM ${endpointRelation}
292
299
  INNER JOIN ${edgesRelation} AS ${q("edges")}
293
300
  ON ${q("__start_key")} = ${edgeFromKey}
@@ -298,10 +305,10 @@ function temporalPathsSelect(route, direction, temporal) {
298
305
  SELECT ${edgeTo}, ${edgeToKey},
299
306
  list_append(${q("routes")}.${q("__visited")}, ${edgeToKey}),
300
307
  list_append(${q("routes")}.${q("__edge_keys")}, ${edgeKey}),
301
- list_append(${q("routes")}.${q("steps")}, ${step(candidateDistance)}),
308
+ list_append(${q("routes")}.${q("steps")}, ${step(candidateDistance, anchor)}),
302
309
  ${candidateDistance}, ${q("edges")}.${q("__event_id")},
303
310
  ${q("edges")}.${q("__event_start")},
304
- ${q("edges")}.${q("__event_end")}
311
+ ${q("edges")}.${q("__event_end")}${elapsedTime ? `, ${anchor}` : ""}
305
312
  FROM ${routesRelation} AS ${q("routes")}
306
313
  INNER JOIN ${edgesRelation} AS ${q("edges")}
307
314
  ON ${q("routes")}.${q("__node_key")} = ${edgeFromKey}
@@ -322,5 +329,5 @@ function temporalPathsSelect(route, direction, temporal) {
322
329
  AS ${q("pathId")}, ${q("steps")}
323
330
  FROM ${completeRelation}
324
331
  )
325
- ${(0, graphRouteResultSelect_js_1.default)(rankedRelation, route.input, route.edgeIdColumn)}`;
332
+ ${(0, graphRouteResultSelect_js_1.default)(rankedRelation, route.input, route.edgeIdColumn, false, elapsedTime)}`;
326
333
  }
@@ -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?: "steps" | "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,OAAO,GAAG,QAAQ,GAAG,aAAa,CAAC;CAC/C,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,CAyHb"}
@@ -38,6 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.default = shortestPath;
40
40
  const buildGraphTemporalCostStateSql_js_1 = __importDefault(require("../helpers/buildGraphTemporalCostStateSql.js"));
41
+ const prepareGraphMetricOptions_js_1 = __importDefault(require("../helpers/prepareGraphMetricOptions.js"));
41
42
  const prepareGraphRouteSql_js_1 = require("../helpers/prepareGraphRouteSql.js");
42
43
  const graphRouteResultSchema_js_1 = __importDefault(require("../helpers/graphRouteResultSchema.js"));
43
44
  const graphRouteResultSelect_js_1 = __importDefault(require("../helpers/graphRouteResultSelect.js"));
@@ -70,6 +71,7 @@ function shortestPath(simpleTable, sourceColumn, targetColumn, edgeId, start, en
70
71
  typeof options.outputTable !== "boolean") {
71
72
  throw new TypeError("shortestPath() options.outputTable must be a string or boolean.");
72
73
  }
74
+ const metrics = (0, prepareGraphMetricOptions_js_1.default)(options, "shortestPath()", true);
73
75
  const endpoints = (0, prepareGraphRouteSql_js_1.prepareGraphRouteEndpoints)(start, end, "shortestPath()");
74
76
  const direction = options.direction ?? "outgoing";
75
77
  const temporalOptions = (0, prepareGraphTemporalSql_js_1.prepareGraphTemporalOptions)(options, direction, "shortestPath()");
@@ -90,7 +92,7 @@ function shortestPath(simpleTable, sourceColumn, targetColumn, edgeId, start, en
90
92
  ? undefined
91
93
  : (input) => (0, validateGraphTemporalEvents_js_1.default)(input, temporalOptions, "shortestPath()", parameters),
92
94
  values: (schema) => {
93
- (0, prepareGraphRouteSql_js_1.validateGraphRouteInputs)(schema, sourceColumn, targetColumn, edgeId, endpoints, options.weight, "shortestPath()");
95
+ (0, prepareGraphRouteSql_js_1.validateGraphRouteInputs)(schema, sourceColumn, targetColumn, edgeId, endpoints, options.weight, "shortestPath()", options.weight !== undefined);
94
96
  const temporal = temporalOptions === undefined
95
97
  ? undefined
96
98
  : (0, prepareGraphTemporalSql_js_1.default)(schema, temporalOptions, "shortestPath()");
@@ -98,29 +100,30 @@ function shortestPath(simpleTable, sourceColumn, targetColumn, edgeId, start, en
98
100
  ? endpoints.values.values
99
101
  : [temporal.gapParameter, ...endpoints.values.values];
100
102
  },
101
- buildSelect: (input, schema) => shortestPathSelect(input, schema, sourceColumn, targetColumn, edgeId, endpoints, direction, options.weight, temporalOptions),
103
+ buildSelect: (input, schema) => shortestPathSelect(input, schema, sourceColumn, targetColumn, edgeId, endpoints, direction, options.weight, temporalOptions, metrics),
102
104
  outputSchema: (schema) => {
103
- const validated = (0, prepareGraphRouteSql_js_1.validateGraphRouteInputs)(schema, sourceColumn, targetColumn, edgeId, endpoints, options.weight, "shortestPath()");
105
+ const validated = (0, prepareGraphRouteSql_js_1.validateGraphRouteInputs)(schema, sourceColumn, targetColumn, edgeId, endpoints, options.weight, "shortestPath()", options.weight !== undefined);
104
106
  if (temporalOptions !== undefined) {
105
107
  (0, prepareGraphTemporalSql_js_1.default)(schema, temporalOptions, "shortestPath()");
106
108
  }
107
- return (0, graphRouteResultSchema_js_1.default)(schema, validated.distanceType, "shortestPath()");
109
+ return (0, graphRouteResultSchema_js_1.default)(schema, validated.distanceType, "shortestPath()", metrics.elapsedTime, options.weight !== undefined);
108
110
  },
109
111
  });
110
112
  }
111
- function shortestPathSelect(input, schema, source, target, edgeId, routeEndpoints, direction, weight, temporalOptions) {
112
- const route = (0, prepareGraphRouteSql_js_1.prepareGraphRouteSql)(input, schema, source, target, edgeId, routeEndpoints, weight, "shortestPath()");
113
+ function shortestPathSelect(input, schema, source, target, edgeId, routeEndpoints, direction, weight, temporalOptions, metrics) {
114
+ const route = (0, prepareGraphRouteSql_js_1.prepareGraphRouteSql)(input, schema, source, target, edgeId, routeEndpoints, weight, "shortestPath()", weight !== undefined);
113
115
  const prepared = route.traversal;
114
116
  if (temporalOptions !== undefined) {
115
117
  const temporal = (0, prepareGraphTemporalSql_js_1.default)(schema, temporalOptions, "shortestPath()");
116
- return temporalShortestPathSelect(route, direction, temporal);
118
+ return temporalShortestPathSelect(route, direction, temporal, metrics, weight !== undefined);
117
119
  }
118
120
  const distanceType = route.distanceType;
119
121
  // Exact shortest routes have optimal prefixes. Floating addition can erase
120
122
  // a prefix-cost difference later, so retain all simple prefixes up to the
121
123
  // best complete cost before selecting the full-route ties.
122
- const floating = route.weightType === "FLOAT" ||
123
- route.weightType === "DOUBLE";
124
+ const floating = metrics.minimize === "weight" &&
125
+ (route.weightType === "FLOAT" || route.weightType === "DOUBLE");
126
+ const metricType = metrics.minimize === "steps" ? "BIGINT" : distanceType;
124
127
  const relations = prepared.relationNames([
125
128
  "graph_route_endpoints",
126
129
  "graph_edges",
@@ -146,8 +149,17 @@ function shortestPathSelect(input, schema, source, target, edgeId, routeEndpoint
146
149
  const edgeToKey = `${q("edges")}.${q("__to_key")}`;
147
150
  const edgeTo = `${q("edges")}.${q("__to")}`;
148
151
  const edgeCost = `${q("edges")}.${q("__weight")}`;
152
+ const edgeMetric = metrics.minimize === "steps"
153
+ ? "CAST(1 AS BIGINT)"
154
+ : edgeCost;
149
155
  const routeDistance = `${q("routes")}.${q("distance")}`;
150
156
  const candidateDistance = `CAST(${routeDistance} + ${edgeCost} AS ${distanceType})`;
157
+ const routeMetric = metrics.minimize === "steps"
158
+ ? `len(${q("routes")}.${q("steps")})`
159
+ : routeDistance;
160
+ const candidateMetric = metrics.minimize === "steps"
161
+ ? `${routeMetric} + 1`
162
+ : candidateDistance;
151
163
  return `WITH RECURSIVE ${endpointRelation}(
152
164
  ${q("start")}, ${q("end")}, ${q("__start_key")}, ${q("__end_key")}
153
165
  ) AS (
@@ -179,13 +191,13 @@ function shortestPathSelect(input, schema, source, target, edgeId, routeEndpoint
179
191
  ${q("node")}, ${q("__node_key")}, ${q("distance")}
180
192
  ) USING KEY(${q("__node_key")}) AS (
181
193
  SELECT ${q("start")}, ${q("__start_key")},
182
- CAST(0 AS ${distanceType})
194
+ CAST(0 AS ${metricType})
183
195
  FROM ${endpointRelation}
184
196
  INNER JOIN ${toEndRelation}
185
197
  ON ${q("__start_key")} = ${q("__key")}
186
198
  UNION
187
199
  SELECT ${edgeTo}, ${edgeToKey},
188
- MIN(CAST(${q("reached")}.${q("distance")} + ${edgeCost} AS ${distanceType}))
200
+ MIN(CAST(${q("reached")}.${q("distance")} + ${edgeMetric} AS ${metricType}))
189
201
  FROM ${distancesRelation} AS ${q("reached")}
190
202
  INNER JOIN ${edgesRelation} AS ${q("edges")}
191
203
  ON ${q("reached")}.${q("__node_key")} = ${edgeFromKey}
@@ -195,7 +207,7 @@ function shortestPathSelect(input, schema, source, target, edgeId, routeEndpoint
195
207
  ON ${edgeToKey} = ${q("best")}.${q("__node_key")}
196
208
  GROUP BY ${edgeTo}, ${edgeToKey}, ${q("best")}.${q("distance")}
197
209
  HAVING ${q("best")}.${q("distance")} IS NULL
198
- OR MIN(CAST(${q("reached")}.${q("distance")} + ${edgeCost} AS ${distanceType})) < ${q("best")}.${q("distance")}
210
+ OR MIN(CAST(${q("reached")}.${q("distance")} + ${edgeMetric} AS ${metricType})) < ${q("best")}.${q("distance")}
199
211
  ), ${bestTotalRelation} AS (
200
212
  SELECT ${q("distances")}.${q("distance")}
201
213
  FROM ${distancesRelation} AS ${q("distances")}
@@ -234,23 +246,23 @@ function shortestPathSelect(input, schema, source, target, edgeId, routeEndpoint
234
246
  WHERE ${q("routes")}.${q("__node_key")} <> ${q("__end_key")}
235
247
  AND NOT list_contains(${q("routes")}.${q("__visited")}, ${edgeToKey})
236
248
  AND ${floating
237
- ? `${candidateDistance} <= ${q("best_total")}.${q("distance")}`
238
- : `${candidateDistance} = ${q("best_prefix")}.${q("distance")}`}
249
+ ? `${candidateMetric} <= ${q("best_total")}.${q("distance")}`
250
+ : `${candidateMetric} = ${q("best_prefix")}.${q("distance")}`}
239
251
  ), ${shortestRelation} AS (
240
252
  SELECT ${q("__edge_keys")}, ${q("steps")}
241
- FROM ${routesRelation}
253
+ FROM ${routesRelation} AS ${q("routes")}
242
254
  CROSS JOIN ${endpointRelation}
243
255
  CROSS JOIN ${bestTotalRelation} AS ${q("best_total")}
244
256
  WHERE ${q("__node_key")} = ${q("__end_key")}
245
- AND ${routesRelation}.${q("distance")} = ${q("best_total")}.${q("distance")}
257
+ AND ${routeMetric} = ${q("best_total")}.${q("distance")}
246
258
  ), ${rankedRelation} AS (
247
259
  SELECT CAST(row_number() OVER (ORDER BY ${q("__edge_keys")}) - 1 AS BIGINT) AS ${q("pathId")},
248
260
  ${q("steps")}
249
261
  FROM ${shortestRelation}
250
262
  )
251
- ${(0, graphRouteResultSelect_js_1.default)(rankedRelation, route.input, route.edgeIdColumn)}`;
263
+ ${(0, graphRouteResultSelect_js_1.default)(rankedRelation, route.input, route.edgeIdColumn, false, false, weight !== undefined)}`;
252
264
  }
253
- function temporalShortestPathSelect(route, direction, temporal) {
265
+ function temporalShortestPathSelect(route, direction, temporal, metrics, includeTotal) {
254
266
  const prepared = route.traversal;
255
267
  const q = quoteIdentifier_js_1.default;
256
268
  const startsSelect = `SELECT ${q("start")},
@@ -258,10 +270,12 @@ function temporalShortestPathSelect(route, direction, temporal) {
258
270
  FROM (SELECT ${route.endpointValue} AS ${q("start")}) AS ${q("values")}`;
259
271
  // Non-negative weights and no maximum gap let cycles be removed from walks.
260
272
  // Their optimum bounds simple routes; retain separate histories for all ties.
261
- const costs = (0, buildGraphTemporalCostStateSql_js_1.default)(prepared, startsSelect, direction, temporal, route.distanceType, route.edgeWeight, [
262
- `${route.typedEdgeId} AS ${q("__edge_id")}`,
263
- `${route.edgeKey} AS ${q("__edge_key")}`,
264
- ]);
273
+ const costs = metrics.minimize === "elapsedTime"
274
+ ? buildElapsedShortestPathCostSql(route, startsSelect, direction, temporal)
275
+ : (0, buildGraphTemporalCostStateSql_js_1.default)(prepared, startsSelect, direction, temporal, route.distanceType, route.edgeWeight, [
276
+ `${route.typedEdgeId} AS ${q("__edge_id")}`,
277
+ `${route.edgeKey} AS ${q("__edge_key")}`,
278
+ ], metrics.minimize === "steps" ? "steps" : "weight");
265
279
  const relations = prepared.relationNames([
266
280
  "graph_route_endpoints",
267
281
  "graph_nodes",
@@ -287,12 +301,33 @@ function temporalShortestPathSelect(route, direction, temporal) {
287
301
  const routeDistance = `${q("routes")}.${q("distance")}`;
288
302
  const candidateDistance = `CAST(${routeDistance} + ${edgeCost} AS ${route.distanceType})`;
289
303
  const gap = `${q("settings")}.${q("__gap")}`;
290
- const step = (distance) => `struct_pack(
304
+ const firstAnchor = temporal.journeyAnchor("edges", direction);
305
+ const routeAnchor = `${q("routes")}.${q("__journey_anchor")}`;
306
+ const firstElapsed = temporal.journeyElapsed("edges", firstAnchor, direction);
307
+ const candidateElapsed = temporal.journeyElapsed("edges", routeAnchor, direction);
308
+ const firstMetric = metrics.minimize === "steps"
309
+ ? "1"
310
+ : metrics.minimize === "elapsedTime"
311
+ ? firstElapsed
312
+ : edgeCost;
313
+ const routeMetric = metrics.minimize === "steps"
314
+ ? `len(${q("routes")}.${q("steps")})`
315
+ : metrics.minimize === "elapsedTime"
316
+ ? `${q("routes")}.${q("__elapsed")}`
317
+ : routeDistance;
318
+ const candidateMetric = metrics.minimize === "steps"
319
+ ? `${routeMetric} + 1`
320
+ : metrics.minimize === "elapsedTime"
321
+ ? candidateElapsed
322
+ : candidateDistance;
323
+ const step = (distance, elapsed) => `struct_pack(
291
324
  ${q("edgeId")} := ${q("edges")}.${q("__edge_id")},
292
325
  ${q("source")} := ${q("edges")}.${q("__from")},
293
326
  ${q("target")} := ${edgeTo},
294
327
  ${q("weight")} := ${edgeCost},
295
- ${q("distance")} := ${distance}
328
+ ${q("distance")} := ${distance}${metrics.elapsedTime
329
+ ? `, ${q("elapsedTimeMs")} := ${temporal.elapsedMilliseconds(elapsed)}`
330
+ : ""}
296
331
  )`;
297
332
  return `${costs.withClause}, ${endpointRelation}(
298
333
  ${q("start")}, ${q("end")}, ${q("__start_key")}, ${q("__end_key")}
@@ -317,44 +352,44 @@ function temporalShortestPathSelect(route, direction, temporal) {
317
352
  INNER JOIN ${edgesRelation} AS ${q("edges")}
318
353
  ON ${q("reached")}.${q("__key")} = ${edgeToKey}
319
354
  ), ${bestTotalRelation} AS (
320
- SELECT MIN(${q("costs")}.${q("distance")}) AS ${q("distance")}
355
+ SELECT MIN(${q("costs")}.${q(metrics.minimize === "steps" ? "steps" : "distance")}) AS ${q("distance")}
321
356
  FROM ${costs.costRelation} AS ${q("costs")}
322
357
  INNER JOIN ${endpointRelation}
323
358
  ON ${q("costs")}.${q("__node_key")} = ${q("__end_key")}
324
359
  ), ${routesRelation}(
325
360
  ${q("node")}, ${q("__node_key")}, ${q("__visited")},
326
361
  ${q("__edge_keys")}, ${q("steps")}, ${q("distance")},
327
- ${q("__event_id")}, ${q("__event_start")}, ${q("__event_end")}
362
+ ${q("__event_id")}, ${q("__event_start")}, ${q("__event_end")}${metrics.elapsedTime ? `, ${q("__journey_anchor")}, ${q("__elapsed")}` : ""}
328
363
  ) AS (
329
364
  SELECT ${edgeTo}, ${edgeToKey},
330
365
  [${q("__start_key")}, ${edgeToKey}], [${edgeKey}],
331
- [${step(edgeCost)}], ${edgeCost},
366
+ [${step(edgeCost, firstElapsed)}], ${edgeCost},
332
367
  ${q("edges")}.${q("__event_id")},
333
368
  ${q("edges")}.${q("__event_start")},
334
- ${q("edges")}.${q("__event_end")}
369
+ ${q("edges")}.${q("__event_end")}${metrics.elapsedTime ? `, ${firstAnchor}, ${firstElapsed}` : ""}
335
370
  FROM ${endpointRelation}
336
371
  INNER JOIN ${edgesRelation} AS ${q("edges")}
337
372
  ON ${q("__start_key")} = ${edgeFromKey}
338
373
  INNER JOIN ${toEndRelation} AS ${q("can_reach_end")}
339
374
  ON ${edgeToKey} = ${q("can_reach_end")}.${q("__key")}
340
375
  INNER JOIN ${bestTotalRelation} AS ${q("best_total")}
341
- ON ${edgeCost} <= ${q("best_total")}.${q("distance")}
376
+ ON ${firstMetric} <= ${q("best_total")}.${q("distance")}
342
377
  WHERE ${q("__start_key")} <> ${edgeToKey}
343
378
  UNION ALL
344
379
  SELECT ${edgeTo}, ${edgeToKey},
345
380
  list_append(${q("routes")}.${q("__visited")}, ${edgeToKey}),
346
381
  list_append(${q("routes")}.${q("__edge_keys")}, ${edgeKey}),
347
- list_append(${q("routes")}.${q("steps")}, ${step(candidateDistance)}),
382
+ list_append(${q("routes")}.${q("steps")}, ${step(candidateDistance, candidateElapsed)}),
348
383
  ${candidateDistance}, ${q("edges")}.${q("__event_id")},
349
384
  ${q("edges")}.${q("__event_start")},
350
- ${q("edges")}.${q("__event_end")}
385
+ ${q("edges")}.${q("__event_end")}${metrics.elapsedTime ? `, ${routeAnchor}, ${candidateElapsed}` : ""}
351
386
  FROM ${routesRelation} AS ${q("routes")}
352
387
  INNER JOIN ${edgesRelation} AS ${q("edges")}
353
388
  ON ${q("routes")}.${q("__node_key")} = ${edgeFromKey}
354
389
  INNER JOIN ${toEndRelation} AS ${q("can_reach_end")}
355
390
  ON ${edgeToKey} = ${q("can_reach_end")}.${q("__key")}
356
391
  INNER JOIN ${bestTotalRelation} AS ${q("best_total")}
357
- ON ${candidateDistance} <= ${q("best_total")}.${q("distance")}
392
+ ON ${candidateMetric} <= ${q("best_total")}.${q("distance")}
358
393
  CROSS JOIN ${endpointRelation}
359
394
  CROSS JOIN ${costs.settingsRelation} AS ${q("settings")}
360
395
  WHERE ${q("routes")}.${q("__node_key")} <> ${q("__end_key")}
@@ -366,11 +401,97 @@ function temporalShortestPathSelect(route, direction, temporal) {
366
401
  CROSS JOIN ${endpointRelation}
367
402
  CROSS JOIN ${bestTotalRelation} AS ${q("best_total")}
368
403
  WHERE ${q("__node_key")} = ${q("__end_key")}
369
- AND ${routeDistance} = ${q("best_total")}.${q("distance")}
404
+ AND ${routeMetric} = ${q("best_total")}.${q("distance")}
370
405
  ), ${rankedRelation} AS (
371
406
  SELECT CAST(row_number() OVER (ORDER BY ${q("__edge_keys")}) - 1 AS BIGINT)
372
407
  AS ${q("pathId")}, ${q("steps")}
373
408
  FROM ${shortestRelation}
374
409
  )
375
- ${(0, graphRouteResultSelect_js_1.default)(rankedRelation, route.input, route.edgeIdColumn)}`;
410
+ ${(0, graphRouteResultSelect_js_1.default)(rankedRelation, route.input, route.edgeIdColumn, false, metrics.elapsedTime, includeTotal)}`;
411
+ }
412
+ // At the same physical event a later departure (or earlier incoming arrival)
413
+ // dominates every future elapsed duration. Keyed improvements find the optimum
414
+ // without enumerating route histories; reconstruction still retains all ties.
415
+ function buildElapsedShortestPathCostSql(route, startsSelect, direction, temporal) {
416
+ const prepared = route.traversal;
417
+ const q = quoteIdentifier_js_1.default;
418
+ const relations = prepared.relationNames([
419
+ "graph_starts",
420
+ "graph_temporal_settings",
421
+ "graph_event_rows",
422
+ "graph_edges",
423
+ "graph_cost_states",
424
+ ]);
425
+ const startsRelation = relations.graph_starts;
426
+ const settingsRelation = relations.graph_temporal_settings;
427
+ const eventRowsRelation = relations.graph_event_rows;
428
+ const edgesRelation = relations.graph_edges;
429
+ const costRelation = relations.graph_cost_states;
430
+ const start = `${q("starts")}.${q("start")}`;
431
+ const startKey = `${q("starts")}.${q("__key")}`;
432
+ const reachedStart = `${q("reached")}.${q("start")}`;
433
+ const reachedStartKey = `${q("reached")}.${q("__start_key")}`;
434
+ const reachedNodeKey = `${q("reached")}.${q("__node_key")}`;
435
+ const edgeTo = `${q("edges")}.${q("__to")}`;
436
+ const edgeToKey = `${q("edges")}.${q("__to_key")}`;
437
+ const edgeFromKey = `${q("edges")}.${q("__from_key")}`;
438
+ const eventId = `${q("edges")}.${q("__event_id")}`;
439
+ const eventStart = `${q("edges")}.${q("__event_start")}`;
440
+ const eventEnd = `${q("edges")}.${q("__event_end")}`;
441
+ const bestDistance = `${q("best")}.${q("distance")}`;
442
+ const gap = `${q("settings")}.${q("__gap")}`;
443
+ const firstAnchor = temporal.journeyAnchor("edges", direction);
444
+ const reachedAnchor = `${q("reached")}.${q("__journey_anchor")}`;
445
+ const firstElapsed = temporal.journeyElapsed("edges", firstAnchor, direction);
446
+ const candidateDistance = temporal.journeyElapsed("edges", reachedAnchor, direction);
447
+ const bestAnchor = `${direction === "outgoing" ? "MAX" : "MIN"}(${reachedAnchor})`;
448
+ return {
449
+ costRelation,
450
+ edgesRelation,
451
+ settingsRelation,
452
+ startsRelation,
453
+ withClause: `WITH RECURSIVE ${settingsRelation} AS MATERIALIZED (
454
+ SELECT CAST(? AS HUGEINT) AS ${q("__gap")}
455
+ ), ${eventRowsRelation} AS MATERIALIZED (
456
+ ${prepared.edges(direction, [
457
+ `${route.edgeWeight} AS ${q("__weight")}`,
458
+ `${route.typedEdgeId} AS ${q("__edge_id")}`,
459
+ `${route.edgeKey} AS ${q("__edge_key")}`,
460
+ ...temporal.eventSelections("edges"),
461
+ ])}
462
+ ), ${edgesRelation} AS MATERIALIZED (
463
+ SELECT *
464
+ FROM ${eventRowsRelation} AS ${q("events")}
465
+ WHERE ${temporal.eventValidity("events")}
466
+ ), ${startsRelation} AS MATERIALIZED (
467
+ ${startsSelect}
468
+ ), ${costRelation}(
469
+ ${q("start")}, ${q("node")},
470
+ ${q("__start_key")}, ${q("__node_key")},
471
+ ${q("__event_id")}, ${q("__event_start")}, ${q("__event_end")},
472
+ ${q("distance")}, ${q("__journey_anchor")}
473
+ ) USING KEY(${q("__start_key")}, ${q("__event_id")}) AS (
474
+ SELECT ${start}, ${edgeTo}, ${startKey}, ${edgeToKey},
475
+ ${eventId}, ${eventStart}, ${eventEnd}, ${firstElapsed}, ${firstAnchor}
476
+ FROM ${startsRelation} AS ${q("starts")}
477
+ INNER JOIN ${edgesRelation} AS ${q("edges")}
478
+ ON ${startKey} = ${edgeFromKey}
479
+ UNION
480
+ SELECT ${reachedStart}, ${edgeTo}, ${reachedStartKey}, ${edgeToKey},
481
+ ${eventId}, ${eventStart}, ${eventEnd},
482
+ MIN(${candidateDistance}) AS ${q("distance")}, ${bestAnchor}
483
+ FROM ${costRelation} AS ${q("reached")}
484
+ INNER JOIN ${edgesRelation} AS ${q("edges")}
485
+ ON ${reachedNodeKey} = ${edgeFromKey}
486
+ CROSS JOIN ${settingsRelation} AS ${q("settings")}
487
+ LEFT JOIN recurring.${costRelation} AS ${q("best")}
488
+ ON ${reachedStartKey} = ${q("best")}.${q("__start_key")}
489
+ AND ${eventId} = ${q("best")}.${q("__event_id")}
490
+ WHERE ${temporal.transition("reached", "edges", direction, gap)}
491
+ GROUP BY ${reachedStart}, ${edgeTo}, ${reachedStartKey}, ${edgeToKey},
492
+ ${eventId}, ${eventStart}, ${eventEnd}, ${bestDistance}
493
+ HAVING ${bestDistance} IS NULL OR
494
+ MIN(${candidateDistance}) < ${bestDistance}
495
+ )`,
496
+ };
376
497
  }