@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.
- package/esm/class/SimpleTable.d.ts +399 -90
- package/esm/class/SimpleTable.d.ts.map +1 -1
- package/esm/class/SimpleTable.js +393 -90
- package/esm/helpers/buildGraphTemporalCostStateSql.d.ts +1 -1
- package/esm/helpers/buildGraphTemporalCostStateSql.d.ts.map +1 -1
- package/esm/helpers/buildGraphTemporalCostStateSql.js +23 -9
- package/esm/helpers/graphRouteResultSchema.d.ts +1 -1
- package/esm/helpers/graphRouteResultSchema.d.ts.map +1 -1
- package/esm/helpers/graphRouteResultSchema.js +4 -3
- package/esm/helpers/graphRouteResultSelect.d.ts +1 -1
- package/esm/helpers/graphRouteResultSelect.d.ts.map +1 -1
- package/esm/helpers/graphRouteResultSelect.js +5 -2
- package/esm/helpers/prepareGraphMetricOptions.d.ts +14 -0
- package/esm/helpers/prepareGraphMetricOptions.d.ts.map +1 -0
- package/esm/helpers/prepareGraphMetricOptions.js +41 -0
- package/esm/helpers/prepareGraphRouteSql.d.ts +2 -2
- package/esm/helpers/prepareGraphRouteSql.d.ts.map +1 -1
- package/esm/helpers/prepareGraphRouteSql.js +4 -4
- 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/helpers/validateGraphRouteResultSchema.d.ts +1 -1
- package/esm/helpers/validateGraphRouteResultSchema.d.ts.map +1 -1
- package/esm/helpers/validateGraphRouteResultSchema.js +3 -1
- package/esm/methods/distances.d.ts +2 -0
- package/esm/methods/distances.d.ts.map +1 -1
- package/esm/methods/distances.js +274 -20
- 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 +155 -34
- package/llm.md +428 -108
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/script/class/SimpleTable.d.ts +399 -90
- package/script/class/SimpleTable.d.ts.map +1 -1
- package/script/class/SimpleTable.js +393 -90
- package/script/helpers/buildGraphTemporalCostStateSql.d.ts +1 -1
- package/script/helpers/buildGraphTemporalCostStateSql.d.ts.map +1 -1
- package/script/helpers/buildGraphTemporalCostStateSql.js +23 -9
- package/script/helpers/graphRouteResultSchema.d.ts +1 -1
- package/script/helpers/graphRouteResultSchema.d.ts.map +1 -1
- package/script/helpers/graphRouteResultSchema.js +4 -3
- package/script/helpers/graphRouteResultSelect.d.ts +1 -1
- package/script/helpers/graphRouteResultSelect.d.ts.map +1 -1
- package/script/helpers/graphRouteResultSelect.js +5 -2
- package/script/helpers/prepareGraphMetricOptions.d.ts +14 -0
- package/script/helpers/prepareGraphMetricOptions.d.ts.map +1 -0
- package/script/helpers/prepareGraphMetricOptions.js +44 -0
- package/script/helpers/prepareGraphRouteSql.d.ts +2 -2
- package/script/helpers/prepareGraphRouteSql.d.ts.map +1 -1
- package/script/helpers/prepareGraphRouteSql.js +4 -4
- 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/helpers/validateGraphRouteResultSchema.d.ts +1 -1
- package/script/helpers/validateGraphRouteResultSchema.d.ts.map +1 -1
- package/script/helpers/validateGraphRouteResultSchema.js +3 -1
- package/script/methods/distances.d.ts +2 -0
- package/script/methods/distances.d.ts.map +1 -1
- package/script/methods/distances.js +274 -20
- 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 +155 -34
package/script/methods/paths.js
CHANGED
|
@@ -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
|
|
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;
|
|
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 =
|
|
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 ${
|
|
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")} + ${
|
|
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")} + ${
|
|
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
|
-
? `${
|
|
238
|
-
: `${
|
|
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 ${
|
|
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 =
|
|
262
|
-
|
|
263
|
-
|
|
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
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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
|
}
|