@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.
- package/esm/class/SimpleTable.d.ts +249 -23
- package/esm/class/SimpleTable.d.ts.map +1 -1
- package/esm/class/SimpleTable.js +243 -23
- package/esm/helpers/graphRouteResultSchema.d.ts +1 -1
- package/esm/helpers/graphRouteResultSchema.d.ts.map +1 -1
- package/esm/helpers/graphRouteResultSchema.js +3 -2
- package/esm/helpers/graphRouteResultSelect.d.ts +1 -1
- package/esm/helpers/graphRouteResultSelect.d.ts.map +1 -1
- package/esm/helpers/graphRouteResultSelect.js +4 -1
- package/esm/helpers/prepareGraphMetricOptions.d.ts +14 -0
- package/esm/helpers/prepareGraphMetricOptions.d.ts.map +1 -0
- package/esm/helpers/prepareGraphMetricOptions.js +40 -0
- package/esm/helpers/prepareGraphTemporalSql.d.ts +3 -0
- package/esm/helpers/prepareGraphTemporalSql.d.ts.map +1 -1
- package/esm/helpers/prepareGraphTemporalSql.js +7 -0
- package/esm/methods/distances.d.ts +2 -0
- package/esm/methods/distances.d.ts.map +1 -1
- package/esm/methods/distances.js +98 -6
- package/esm/methods/findCycles.d.ts +1 -0
- package/esm/methods/findCycles.d.ts.map +1 -1
- package/esm/methods/findCycles.js +22 -15
- package/esm/methods/paths.d.ts +1 -0
- package/esm/methods/paths.d.ts.map +1 -1
- package/esm/methods/paths.js +21 -14
- package/esm/methods/shortestPath.d.ts +2 -0
- package/esm/methods/shortestPath.d.ts.map +1 -1
- package/esm/methods/shortestPath.js +125 -20
- package/llm.md +278 -53
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/script/class/SimpleTable.d.ts +249 -23
- package/script/class/SimpleTable.d.ts.map +1 -1
- package/script/class/SimpleTable.js +243 -23
- package/script/helpers/graphRouteResultSchema.d.ts +1 -1
- package/script/helpers/graphRouteResultSchema.d.ts.map +1 -1
- package/script/helpers/graphRouteResultSchema.js +3 -2
- package/script/helpers/graphRouteResultSelect.d.ts +1 -1
- package/script/helpers/graphRouteResultSelect.d.ts.map +1 -1
- package/script/helpers/graphRouteResultSelect.js +4 -1
- package/script/helpers/prepareGraphMetricOptions.d.ts +14 -0
- package/script/helpers/prepareGraphMetricOptions.d.ts.map +1 -0
- package/script/helpers/prepareGraphMetricOptions.js +43 -0
- package/script/helpers/prepareGraphTemporalSql.d.ts +3 -0
- package/script/helpers/prepareGraphTemporalSql.d.ts.map +1 -1
- package/script/helpers/prepareGraphTemporalSql.js +7 -0
- package/script/methods/distances.d.ts +2 -0
- package/script/methods/distances.d.ts.map +1 -1
- package/script/methods/distances.js +98 -6
- package/script/methods/findCycles.d.ts +1 -0
- package/script/methods/findCycles.d.ts.map +1 -1
- package/script/methods/findCycles.js +22 -15
- package/script/methods/paths.d.ts +1 -0
- package/script/methods/paths.d.ts.map +1 -1
- package/script/methods/paths.js +21 -14
- package/script/methods/shortestPath.d.ts +2 -0
- package/script/methods/shortestPath.d.ts.map +1 -1
- package/script/methods/shortestPath.js +125 -20
|
@@ -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()");
|
|
@@ -98,22 +100,22 @@ 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
105
|
const validated = (0, prepareGraphRouteSql_js_1.validateGraphRouteInputs)(schema, sourceColumn, targetColumn, edgeId, endpoints, options.weight, "shortestPath()");
|
|
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);
|
|
108
110
|
},
|
|
109
111
|
});
|
|
110
112
|
}
|
|
111
|
-
function shortestPathSelect(input, schema, source, target, edgeId, routeEndpoints, direction, weight, temporalOptions) {
|
|
113
|
+
function shortestPathSelect(input, schema, source, target, edgeId, routeEndpoints, direction, weight, temporalOptions, metrics) {
|
|
112
114
|
const route = (0, prepareGraphRouteSql_js_1.prepareGraphRouteSql)(input, schema, source, target, edgeId, routeEndpoints, weight, "shortestPath()");
|
|
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);
|
|
117
119
|
}
|
|
118
120
|
const distanceType = route.distanceType;
|
|
119
121
|
// Exact shortest routes have optimal prefixes. Floating addition can erase
|
|
@@ -250,7 +252,7 @@ function shortestPathSelect(input, schema, source, target, edgeId, routeEndpoint
|
|
|
250
252
|
)
|
|
251
253
|
${(0, graphRouteResultSelect_js_1.default)(rankedRelation, route.input, route.edgeIdColumn)}`;
|
|
252
254
|
}
|
|
253
|
-
function temporalShortestPathSelect(route, direction, temporal) {
|
|
255
|
+
function temporalShortestPathSelect(route, direction, temporal, metrics) {
|
|
254
256
|
const prepared = route.traversal;
|
|
255
257
|
const q = quoteIdentifier_js_1.default;
|
|
256
258
|
const startsSelect = `SELECT ${q("start")},
|
|
@@ -258,10 +260,12 @@ function temporalShortestPathSelect(route, direction, temporal) {
|
|
|
258
260
|
FROM (SELECT ${route.endpointValue} AS ${q("start")}) AS ${q("values")}`;
|
|
259
261
|
// Non-negative weights and no maximum gap let cycles be removed from walks.
|
|
260
262
|
// Their optimum bounds simple routes; retain separate histories for all ties.
|
|
261
|
-
const costs =
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
263
|
+
const costs = metrics.minimize === "elapsedTime"
|
|
264
|
+
? buildElapsedShortestPathCostSql(route, startsSelect, direction, temporal)
|
|
265
|
+
: (0, buildGraphTemporalCostStateSql_js_1.default)(prepared, startsSelect, direction, temporal, route.distanceType, route.edgeWeight, [
|
|
266
|
+
`${route.typedEdgeId} AS ${q("__edge_id")}`,
|
|
267
|
+
`${route.edgeKey} AS ${q("__edge_key")}`,
|
|
268
|
+
]);
|
|
265
269
|
const relations = prepared.relationNames([
|
|
266
270
|
"graph_route_endpoints",
|
|
267
271
|
"graph_nodes",
|
|
@@ -287,12 +291,27 @@ function temporalShortestPathSelect(route, direction, temporal) {
|
|
|
287
291
|
const routeDistance = `${q("routes")}.${q("distance")}`;
|
|
288
292
|
const candidateDistance = `CAST(${routeDistance} + ${edgeCost} AS ${route.distanceType})`;
|
|
289
293
|
const gap = `${q("settings")}.${q("__gap")}`;
|
|
290
|
-
const
|
|
294
|
+
const firstAnchor = temporal.journeyAnchor("edges", direction);
|
|
295
|
+
const routeAnchor = `${q("routes")}.${q("__journey_anchor")}`;
|
|
296
|
+
const firstElapsed = temporal.journeyElapsed("edges", firstAnchor, direction);
|
|
297
|
+
const candidateElapsed = temporal.journeyElapsed("edges", routeAnchor, direction);
|
|
298
|
+
const firstMetric = metrics.minimize === "elapsedTime"
|
|
299
|
+
? firstElapsed
|
|
300
|
+
: edgeCost;
|
|
301
|
+
const candidateMetric = metrics.minimize === "elapsedTime"
|
|
302
|
+
? candidateElapsed
|
|
303
|
+
: candidateDistance;
|
|
304
|
+
const routeMetric = metrics.minimize === "elapsedTime"
|
|
305
|
+
? `${q("routes")}.${q("__elapsed")}`
|
|
306
|
+
: routeDistance;
|
|
307
|
+
const step = (distance, elapsed) => `struct_pack(
|
|
291
308
|
${q("edgeId")} := ${q("edges")}.${q("__edge_id")},
|
|
292
309
|
${q("source")} := ${q("edges")}.${q("__from")},
|
|
293
310
|
${q("target")} := ${edgeTo},
|
|
294
311
|
${q("weight")} := ${edgeCost},
|
|
295
|
-
${q("distance")} := ${distance}
|
|
312
|
+
${q("distance")} := ${distance}${metrics.elapsedTime
|
|
313
|
+
? `, ${q("elapsedTimeMs")} := ${temporal.elapsedMilliseconds(elapsed)}`
|
|
314
|
+
: ""}
|
|
296
315
|
)`;
|
|
297
316
|
return `${costs.withClause}, ${endpointRelation}(
|
|
298
317
|
${q("start")}, ${q("end")}, ${q("__start_key")}, ${q("__end_key")}
|
|
@@ -324,37 +343,37 @@ function temporalShortestPathSelect(route, direction, temporal) {
|
|
|
324
343
|
), ${routesRelation}(
|
|
325
344
|
${q("node")}, ${q("__node_key")}, ${q("__visited")},
|
|
326
345
|
${q("__edge_keys")}, ${q("steps")}, ${q("distance")},
|
|
327
|
-
${q("__event_id")}, ${q("__event_start")}, ${q("__event_end")}
|
|
346
|
+
${q("__event_id")}, ${q("__event_start")}, ${q("__event_end")}${metrics.elapsedTime ? `, ${q("__journey_anchor")}, ${q("__elapsed")}` : ""}
|
|
328
347
|
) AS (
|
|
329
348
|
SELECT ${edgeTo}, ${edgeToKey},
|
|
330
349
|
[${q("__start_key")}, ${edgeToKey}], [${edgeKey}],
|
|
331
|
-
[${step(edgeCost)}], ${edgeCost},
|
|
350
|
+
[${step(edgeCost, firstElapsed)}], ${edgeCost},
|
|
332
351
|
${q("edges")}.${q("__event_id")},
|
|
333
352
|
${q("edges")}.${q("__event_start")},
|
|
334
|
-
${q("edges")}.${q("__event_end")}
|
|
353
|
+
${q("edges")}.${q("__event_end")}${metrics.elapsedTime ? `, ${firstAnchor}, ${firstElapsed}` : ""}
|
|
335
354
|
FROM ${endpointRelation}
|
|
336
355
|
INNER JOIN ${edgesRelation} AS ${q("edges")}
|
|
337
356
|
ON ${q("__start_key")} = ${edgeFromKey}
|
|
338
357
|
INNER JOIN ${toEndRelation} AS ${q("can_reach_end")}
|
|
339
358
|
ON ${edgeToKey} = ${q("can_reach_end")}.${q("__key")}
|
|
340
359
|
INNER JOIN ${bestTotalRelation} AS ${q("best_total")}
|
|
341
|
-
ON ${
|
|
360
|
+
ON ${firstMetric} <= ${q("best_total")}.${q("distance")}
|
|
342
361
|
WHERE ${q("__start_key")} <> ${edgeToKey}
|
|
343
362
|
UNION ALL
|
|
344
363
|
SELECT ${edgeTo}, ${edgeToKey},
|
|
345
364
|
list_append(${q("routes")}.${q("__visited")}, ${edgeToKey}),
|
|
346
365
|
list_append(${q("routes")}.${q("__edge_keys")}, ${edgeKey}),
|
|
347
|
-
list_append(${q("routes")}.${q("steps")}, ${step(candidateDistance)}),
|
|
366
|
+
list_append(${q("routes")}.${q("steps")}, ${step(candidateDistance, candidateElapsed)}),
|
|
348
367
|
${candidateDistance}, ${q("edges")}.${q("__event_id")},
|
|
349
368
|
${q("edges")}.${q("__event_start")},
|
|
350
|
-
${q("edges")}.${q("__event_end")}
|
|
369
|
+
${q("edges")}.${q("__event_end")}${metrics.elapsedTime ? `, ${routeAnchor}, ${candidateElapsed}` : ""}
|
|
351
370
|
FROM ${routesRelation} AS ${q("routes")}
|
|
352
371
|
INNER JOIN ${edgesRelation} AS ${q("edges")}
|
|
353
372
|
ON ${q("routes")}.${q("__node_key")} = ${edgeFromKey}
|
|
354
373
|
INNER JOIN ${toEndRelation} AS ${q("can_reach_end")}
|
|
355
374
|
ON ${edgeToKey} = ${q("can_reach_end")}.${q("__key")}
|
|
356
375
|
INNER JOIN ${bestTotalRelation} AS ${q("best_total")}
|
|
357
|
-
ON ${
|
|
376
|
+
ON ${candidateMetric} <= ${q("best_total")}.${q("distance")}
|
|
358
377
|
CROSS JOIN ${endpointRelation}
|
|
359
378
|
CROSS JOIN ${costs.settingsRelation} AS ${q("settings")}
|
|
360
379
|
WHERE ${q("routes")}.${q("__node_key")} <> ${q("__end_key")}
|
|
@@ -366,11 +385,97 @@ function temporalShortestPathSelect(route, direction, temporal) {
|
|
|
366
385
|
CROSS JOIN ${endpointRelation}
|
|
367
386
|
CROSS JOIN ${bestTotalRelation} AS ${q("best_total")}
|
|
368
387
|
WHERE ${q("__node_key")} = ${q("__end_key")}
|
|
369
|
-
AND ${
|
|
388
|
+
AND ${routeMetric} = ${q("best_total")}.${q("distance")}
|
|
370
389
|
), ${rankedRelation} AS (
|
|
371
390
|
SELECT CAST(row_number() OVER (ORDER BY ${q("__edge_keys")}) - 1 AS BIGINT)
|
|
372
391
|
AS ${q("pathId")}, ${q("steps")}
|
|
373
392
|
FROM ${shortestRelation}
|
|
374
393
|
)
|
|
375
|
-
${(0, graphRouteResultSelect_js_1.default)(rankedRelation, route.input, route.edgeIdColumn)}`;
|
|
394
|
+
${(0, graphRouteResultSelect_js_1.default)(rankedRelation, route.input, route.edgeIdColumn, false, metrics.elapsedTime)}`;
|
|
395
|
+
}
|
|
396
|
+
// At the same physical event a later departure (or earlier incoming arrival)
|
|
397
|
+
// dominates every future elapsed duration. Keyed improvements find the optimum
|
|
398
|
+
// without enumerating route histories; reconstruction still retains all ties.
|
|
399
|
+
function buildElapsedShortestPathCostSql(route, startsSelect, direction, temporal) {
|
|
400
|
+
const prepared = route.traversal;
|
|
401
|
+
const q = quoteIdentifier_js_1.default;
|
|
402
|
+
const relations = prepared.relationNames([
|
|
403
|
+
"graph_starts",
|
|
404
|
+
"graph_temporal_settings",
|
|
405
|
+
"graph_event_rows",
|
|
406
|
+
"graph_edges",
|
|
407
|
+
"graph_cost_states",
|
|
408
|
+
]);
|
|
409
|
+
const startsRelation = relations.graph_starts;
|
|
410
|
+
const settingsRelation = relations.graph_temporal_settings;
|
|
411
|
+
const eventRowsRelation = relations.graph_event_rows;
|
|
412
|
+
const edgesRelation = relations.graph_edges;
|
|
413
|
+
const costRelation = relations.graph_cost_states;
|
|
414
|
+
const start = `${q("starts")}.${q("start")}`;
|
|
415
|
+
const startKey = `${q("starts")}.${q("__key")}`;
|
|
416
|
+
const reachedStart = `${q("reached")}.${q("start")}`;
|
|
417
|
+
const reachedStartKey = `${q("reached")}.${q("__start_key")}`;
|
|
418
|
+
const reachedNodeKey = `${q("reached")}.${q("__node_key")}`;
|
|
419
|
+
const edgeTo = `${q("edges")}.${q("__to")}`;
|
|
420
|
+
const edgeToKey = `${q("edges")}.${q("__to_key")}`;
|
|
421
|
+
const edgeFromKey = `${q("edges")}.${q("__from_key")}`;
|
|
422
|
+
const eventId = `${q("edges")}.${q("__event_id")}`;
|
|
423
|
+
const eventStart = `${q("edges")}.${q("__event_start")}`;
|
|
424
|
+
const eventEnd = `${q("edges")}.${q("__event_end")}`;
|
|
425
|
+
const bestDistance = `${q("best")}.${q("distance")}`;
|
|
426
|
+
const gap = `${q("settings")}.${q("__gap")}`;
|
|
427
|
+
const firstAnchor = temporal.journeyAnchor("edges", direction);
|
|
428
|
+
const reachedAnchor = `${q("reached")}.${q("__journey_anchor")}`;
|
|
429
|
+
const firstElapsed = temporal.journeyElapsed("edges", firstAnchor, direction);
|
|
430
|
+
const candidateDistance = temporal.journeyElapsed("edges", reachedAnchor, direction);
|
|
431
|
+
const bestAnchor = `${direction === "outgoing" ? "MAX" : "MIN"}(${reachedAnchor})`;
|
|
432
|
+
return {
|
|
433
|
+
costRelation,
|
|
434
|
+
edgesRelation,
|
|
435
|
+
settingsRelation,
|
|
436
|
+
startsRelation,
|
|
437
|
+
withClause: `WITH RECURSIVE ${settingsRelation} AS MATERIALIZED (
|
|
438
|
+
SELECT CAST(? AS HUGEINT) AS ${q("__gap")}
|
|
439
|
+
), ${eventRowsRelation} AS MATERIALIZED (
|
|
440
|
+
${prepared.edges(direction, [
|
|
441
|
+
`${route.edgeWeight} AS ${q("__weight")}`,
|
|
442
|
+
`${route.typedEdgeId} AS ${q("__edge_id")}`,
|
|
443
|
+
`${route.edgeKey} AS ${q("__edge_key")}`,
|
|
444
|
+
...temporal.eventSelections("edges"),
|
|
445
|
+
])}
|
|
446
|
+
), ${edgesRelation} AS MATERIALIZED (
|
|
447
|
+
SELECT *
|
|
448
|
+
FROM ${eventRowsRelation} AS ${q("events")}
|
|
449
|
+
WHERE ${temporal.eventValidity("events")}
|
|
450
|
+
), ${startsRelation} AS MATERIALIZED (
|
|
451
|
+
${startsSelect}
|
|
452
|
+
), ${costRelation}(
|
|
453
|
+
${q("start")}, ${q("node")},
|
|
454
|
+
${q("__start_key")}, ${q("__node_key")},
|
|
455
|
+
${q("__event_id")}, ${q("__event_start")}, ${q("__event_end")},
|
|
456
|
+
${q("distance")}, ${q("__journey_anchor")}
|
|
457
|
+
) USING KEY(${q("__start_key")}, ${q("__event_id")}) AS (
|
|
458
|
+
SELECT ${start}, ${edgeTo}, ${startKey}, ${edgeToKey},
|
|
459
|
+
${eventId}, ${eventStart}, ${eventEnd}, ${firstElapsed}, ${firstAnchor}
|
|
460
|
+
FROM ${startsRelation} AS ${q("starts")}
|
|
461
|
+
INNER JOIN ${edgesRelation} AS ${q("edges")}
|
|
462
|
+
ON ${startKey} = ${edgeFromKey}
|
|
463
|
+
UNION
|
|
464
|
+
SELECT ${reachedStart}, ${edgeTo}, ${reachedStartKey}, ${edgeToKey},
|
|
465
|
+
${eventId}, ${eventStart}, ${eventEnd},
|
|
466
|
+
MIN(${candidateDistance}) AS ${q("distance")}, ${bestAnchor}
|
|
467
|
+
FROM ${costRelation} AS ${q("reached")}
|
|
468
|
+
INNER JOIN ${edgesRelation} AS ${q("edges")}
|
|
469
|
+
ON ${reachedNodeKey} = ${edgeFromKey}
|
|
470
|
+
CROSS JOIN ${settingsRelation} AS ${q("settings")}
|
|
471
|
+
LEFT JOIN recurring.${costRelation} AS ${q("best")}
|
|
472
|
+
ON ${reachedStartKey} = ${q("best")}.${q("__start_key")}
|
|
473
|
+
AND ${eventId} = ${q("best")}.${q("__event_id")}
|
|
474
|
+
WHERE ${temporal.transition("reached", "edges", direction, gap)}
|
|
475
|
+
GROUP BY ${reachedStart}, ${edgeTo}, ${reachedStartKey}, ${edgeToKey},
|
|
476
|
+
${eventId}, ${eventStart}, ${eventEnd}, ${bestDistance}
|
|
477
|
+
HAVING ${bestDistance} IS NULL OR
|
|
478
|
+
MIN(${candidateDistance}) < ${bestDistance}
|
|
479
|
+
)`,
|
|
480
|
+
};
|
|
376
481
|
}
|