@nshiab/simple-data-analysis-core 2.1.7 → 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 +187 -104
- package/esm/class/SimpleTable.d.ts.map +1 -1
- package/esm/class/SimpleTable.js +185 -102
- 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 +3 -3
- package/esm/helpers/graphRouteResultSelect.d.ts +1 -1
- package/esm/helpers/graphRouteResultSelect.d.ts.map +1 -1
- package/esm/helpers/graphRouteResultSelect.js +2 -2
- package/esm/helpers/prepareGraphMetricOptions.d.ts +2 -2
- package/esm/helpers/prepareGraphMetricOptions.d.ts.map +1 -1
- package/esm/helpers/prepareGraphMetricOptions.js +5 -4
- 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/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 +1 -1
- package/esm/methods/distances.d.ts.map +1 -1
- package/esm/methods/distances.js +235 -73
- package/esm/methods/shortestPath.d.ts +1 -1
- package/esm/methods/shortestPath.d.ts.map +1 -1
- package/esm/methods/shortestPath.js +44 -28
- package/llm.md +207 -112
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/script/class/SimpleTable.d.ts +187 -104
- package/script/class/SimpleTable.d.ts.map +1 -1
- package/script/class/SimpleTable.js +185 -102
- 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 +3 -3
- package/script/helpers/graphRouteResultSelect.d.ts +1 -1
- package/script/helpers/graphRouteResultSelect.d.ts.map +1 -1
- package/script/helpers/graphRouteResultSelect.js +2 -2
- package/script/helpers/prepareGraphMetricOptions.d.ts +2 -2
- package/script/helpers/prepareGraphMetricOptions.d.ts.map +1 -1
- package/script/helpers/prepareGraphMetricOptions.js +5 -4
- 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/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 +1 -1
- package/script/methods/distances.d.ts.map +1 -1
- package/script/methods/distances.js +235 -73
- package/script/methods/shortestPath.d.ts +1 -1
- package/script/methods/shortestPath.d.ts.map +1 -1
- package/script/methods/shortestPath.js +44 -28
|
@@ -98,7 +98,10 @@ function distances(simpleTable, sourceColumn, targetColumn, startNodes, options
|
|
|
98
98
|
return {
|
|
99
99
|
start: validated.idType,
|
|
100
100
|
node: validated.idType,
|
|
101
|
-
|
|
101
|
+
steps: "BIGINT",
|
|
102
|
+
...(options.weight === undefined
|
|
103
|
+
? {}
|
|
104
|
+
: { total: validated.distanceType }),
|
|
102
105
|
...(metrics.elapsedTime ? { elapsedTimeMs: "DOUBLE" } : {}),
|
|
103
106
|
};
|
|
104
107
|
},
|
|
@@ -123,12 +126,14 @@ function distancesSelect(input, schema, source, target, starts, direction, weigh
|
|
|
123
126
|
const edgeWeight = weightColumn === undefined
|
|
124
127
|
? `CAST(1 AS ${distanceType})`
|
|
125
128
|
: `CAST(${(0, quoteIdentifier_js_1.default)("edges")}.${(0, quoteIdentifier_js_1.default)(weightColumn.column)} AS ${distanceType})`;
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
const temporal = temporalOptions === undefined
|
|
130
|
+
? undefined
|
|
131
|
+
: (0, prepareGraphTemporalSql_js_1.default)(schema, temporalOptions, "distances()");
|
|
132
|
+
if (weight !== undefined || metrics.elapsedTime) {
|
|
133
|
+
return summaryDistancesSelect(prepared, direction, temporal, distanceType, edgeWeight, metrics, weight !== undefined, weightColumn?.type === "FLOAT" || weightColumn?.type === "DOUBLE");
|
|
134
|
+
}
|
|
135
|
+
if (temporal !== undefined) {
|
|
136
|
+
return temporalDistancesSelect(prepared, direction, temporal, distanceType, edgeWeight, "steps", false);
|
|
132
137
|
}
|
|
133
138
|
const relations = prepared.relationNames([
|
|
134
139
|
"graph_start_values",
|
|
@@ -152,6 +157,14 @@ function distancesSelect(input, schema, source, target, starts, direction, weigh
|
|
|
152
157
|
const edgeCost = `${(0, quoteIdentifier_js_1.default)("edges")}.${(0, quoteIdentifier_js_1.default)("__weight")}`;
|
|
153
158
|
const bestDistance = `${(0, quoteIdentifier_js_1.default)("best")}.${(0, quoteIdentifier_js_1.default)("distance")}`;
|
|
154
159
|
const candidateDistance = `CAST(${reachedDistance} + ${edgeCost} AS ${distanceType})`;
|
|
160
|
+
const candidateSteps = `${(0, quoteIdentifier_js_1.default)("reached")}.${(0, quoteIdentifier_js_1.default)("steps")} + 1`;
|
|
161
|
+
const bestSteps = `${(0, quoteIdentifier_js_1.default)("best")}.${(0, quoteIdentifier_js_1.default)("steps")}`;
|
|
162
|
+
const order = (distance, steps) => metrics.minimize === "steps"
|
|
163
|
+
? `ROW(${steps}, ${distance})`
|
|
164
|
+
: `ROW(${distance}, ${steps})`;
|
|
165
|
+
const candidateOrder = order(candidateDistance, candidateSteps);
|
|
166
|
+
const selectedDistance = `arg_min(${candidateDistance}, ${candidateOrder})`;
|
|
167
|
+
const selectedSteps = `arg_min(${candidateSteps}, ${candidateOrder})`;
|
|
155
168
|
return `WITH RECURSIVE ${startValuesRelation}(${(0, quoteIdentifier_js_1.default)("start")}) AS (
|
|
156
169
|
VALUES ${prepared.startValues}
|
|
157
170
|
), ${startsRelation} AS (
|
|
@@ -165,19 +178,20 @@ function distancesSelect(input, schema, source, target, starts, direction, weigh
|
|
|
165
178
|
), ${distancesRelation}(
|
|
166
179
|
${(0, quoteIdentifier_js_1.default)("start")}, ${(0, quoteIdentifier_js_1.default)("node")},
|
|
167
180
|
${(0, quoteIdentifier_js_1.default)("__start_key")}, ${(0, quoteIdentifier_js_1.default)("__node_key")},
|
|
168
|
-
${(0, quoteIdentifier_js_1.default)("distance")}
|
|
181
|
+
${(0, quoteIdentifier_js_1.default)("distance")}, ${(0, quoteIdentifier_js_1.default)("steps")}
|
|
169
182
|
) USING KEY(
|
|
170
183
|
${(0, quoteIdentifier_js_1.default)("__start_key")}, ${(0, quoteIdentifier_js_1.default)("__node_key")}
|
|
171
184
|
) AS (
|
|
172
185
|
SELECT ${start}, ${edgeTo}, ${startKey}, ${edgeToKey},
|
|
173
|
-
MIN(${edgeCost}) AS ${(0, quoteIdentifier_js_1.default)("distance")}
|
|
186
|
+
MIN(${edgeCost}) AS ${(0, quoteIdentifier_js_1.default)("distance")}, CAST(1 AS BIGINT)
|
|
174
187
|
FROM ${startsRelation} AS ${(0, quoteIdentifier_js_1.default)("starts")}
|
|
175
188
|
INNER JOIN ${edgesRelation} AS ${(0, quoteIdentifier_js_1.default)("edges")}
|
|
176
189
|
ON ${startKey} = ${edgeFromKey}
|
|
177
190
|
GROUP BY ${start}, ${edgeTo}, ${startKey}, ${edgeToKey}
|
|
178
191
|
UNION
|
|
179
192
|
SELECT ${reachedStart}, ${edgeTo}, ${reachedStartKey}, ${edgeToKey},
|
|
180
|
-
|
|
193
|
+
${selectedDistance} AS ${(0, quoteIdentifier_js_1.default)("distance")},
|
|
194
|
+
${selectedSteps} AS ${(0, quoteIdentifier_js_1.default)("steps")}
|
|
181
195
|
FROM ${distancesRelation} AS ${(0, quoteIdentifier_js_1.default)("reached")}
|
|
182
196
|
INNER JOIN ${edgesRelation} AS ${(0, quoteIdentifier_js_1.default)("edges")}
|
|
183
197
|
ON ${reachedNodeKey} = ${edgeFromKey}
|
|
@@ -185,111 +199,259 @@ function distancesSelect(input, schema, source, target, starts, direction, weigh
|
|
|
185
199
|
ON ${reachedStartKey} = ${(0, quoteIdentifier_js_1.default)("best")}.${(0, quoteIdentifier_js_1.default)("__start_key")}
|
|
186
200
|
AND ${edgeToKey} = ${(0, quoteIdentifier_js_1.default)("best")}.${(0, quoteIdentifier_js_1.default)("__node_key")}
|
|
187
201
|
GROUP BY ${reachedStart}, ${edgeTo}, ${reachedStartKey}, ${edgeToKey},
|
|
188
|
-
${bestDistance}
|
|
189
|
-
HAVING ${bestDistance} IS NULL OR
|
|
202
|
+
${bestDistance}, ${bestSteps}
|
|
203
|
+
HAVING ${bestDistance} IS NULL OR
|
|
204
|
+
${order(selectedDistance, selectedSteps)} < ${order(bestDistance, bestSteps)}
|
|
190
205
|
)
|
|
191
206
|
SELECT ${(0, quoteIdentifier_js_1.default)("start")}, ${(0, quoteIdentifier_js_1.default)("node")},
|
|
192
|
-
${(0, quoteIdentifier_js_1.default)("
|
|
207
|
+
${(0, quoteIdentifier_js_1.default)("steps")}${weight === undefined
|
|
208
|
+
? ""
|
|
209
|
+
: `, ${(0, quoteIdentifier_js_1.default)("distance")} AS ${(0, quoteIdentifier_js_1.default)("total")}`}
|
|
193
210
|
FROM ${distancesRelation}
|
|
194
|
-
ORDER BY ${(0, quoteIdentifier_js_1.default)("__start_key")}, ${(0, quoteIdentifier_js_1.default)("distance")}, ${(0, quoteIdentifier_js_1.default)("__node_key")}`;
|
|
211
|
+
ORDER BY ${(0, quoteIdentifier_js_1.default)("__start_key")}, ${(0, quoteIdentifier_js_1.default)(metrics.minimize === "steps" ? "steps" : "distance")}, ${(0, quoteIdentifier_js_1.default)("__node_key")}`;
|
|
195
212
|
}
|
|
196
|
-
function temporalDistancesSelect(prepared, direction, temporal, distanceType, edgeWeight) {
|
|
213
|
+
function temporalDistancesSelect(prepared, direction, temporal, distanceType, edgeWeight, minimize, includeTotal) {
|
|
197
214
|
const q = quoteIdentifier_js_1.default;
|
|
198
215
|
const startsSelect = `SELECT ${q("start")},
|
|
199
216
|
${prepared.key(q("start"))} AS ${q("__key")}
|
|
200
217
|
FROM (VALUES ${prepared.startValues}) AS ${q("start_values")}(${q("start")})`;
|
|
201
|
-
const costStates = (0, buildGraphTemporalCostStateSql_js_1.default)(prepared, startsSelect, direction, temporal, distanceType, edgeWeight);
|
|
218
|
+
const costStates = (0, buildGraphTemporalCostStateSql_js_1.default)(prepared, startsSelect, direction, temporal, distanceType, edgeWeight, [], minimize);
|
|
219
|
+
const order = minimize === "steps"
|
|
220
|
+
? `${q("steps")}, ${q("distance")}`
|
|
221
|
+
: `${q("distance")}, ${q("steps")}`;
|
|
202
222
|
return `${costStates.withClause}
|
|
203
|
-
SELECT ${q("start")}, ${q("node")},
|
|
204
|
-
MIN(${q("distance")}) AS ${q("total")}
|
|
223
|
+
SELECT ${q("start")}, ${q("node")}, ${q("steps")}${includeTotal ? `, ${q("distance")} AS ${q("total")}` : ""}
|
|
205
224
|
FROM ${costStates.costRelation}
|
|
206
|
-
|
|
207
|
-
${q("__node_key")}
|
|
208
|
-
|
|
225
|
+
QUALIFY row_number() OVER (
|
|
226
|
+
PARTITION BY ${q("__start_key")}, ${q("__node_key")}
|
|
227
|
+
ORDER BY ${order}
|
|
228
|
+
) = 1
|
|
229
|
+
ORDER BY ${q("__start_key")}, ${q(minimize === "steps" ? "steps" : "distance")}, ${q("__node_key")}`;
|
|
209
230
|
}
|
|
210
|
-
|
|
231
|
+
// Distinct visited sets, metrics, and temporal boundaries are enough to extend
|
|
232
|
+
// summaries. No edge IDs or full connection sequences are needed. Keeping the
|
|
233
|
+
// visited set is necessary: equal summaries can have different legal extensions.
|
|
234
|
+
function summaryDistancesSelect(prepared, direction, temporal, distanceType, edgeWeight, metrics, includeTotal, floatingWeight) {
|
|
211
235
|
const q = quoteIdentifier_js_1.default;
|
|
212
236
|
const relations = prepared.relationNames([
|
|
213
237
|
"graph_starts",
|
|
214
238
|
"graph_temporal_settings",
|
|
215
239
|
"graph_event_rows",
|
|
216
240
|
"graph_edges",
|
|
217
|
-
"
|
|
218
|
-
"
|
|
241
|
+
"graph_summaries",
|
|
242
|
+
"graph_metric_results",
|
|
243
|
+
"graph_tied_summaries",
|
|
244
|
+
"graph_best_metrics",
|
|
245
|
+
"graph_metric_bounds",
|
|
219
246
|
]);
|
|
220
247
|
const starts = relations.graph_starts;
|
|
221
248
|
const settings = relations.graph_temporal_settings;
|
|
222
249
|
const events = relations.graph_event_rows;
|
|
223
250
|
const edges = relations.graph_edges;
|
|
224
|
-
const
|
|
225
|
-
const results = relations.
|
|
251
|
+
const summaries = relations.graph_summaries;
|
|
252
|
+
const results = relations.graph_metric_results;
|
|
253
|
+
const tied = relations.graph_tied_summaries;
|
|
226
254
|
const ref = (alias, column) => `${q(alias)}.${q(column)}`;
|
|
227
|
-
const
|
|
228
|
-
const
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
255
|
+
const searchDirection = direction;
|
|
256
|
+
const firstAnchor = temporal?.journeyAnchor("edges", searchDirection);
|
|
257
|
+
const sum = `CAST(${ref("reached", "distance")} + ${ref("edges", "__weight")} AS ${distanceType})`;
|
|
258
|
+
// Reported costs must not reject routes before the primary objective has
|
|
259
|
+
// selected its results. NULL marks overflow and propagates through sums;
|
|
260
|
+
// non-negative weights cannot bring an overflowing total back into range.
|
|
261
|
+
const nextDistance = metrics.minimize === "weight" ? sum : `TRY(${sum})`;
|
|
262
|
+
const temporalColumns = temporal === undefined
|
|
263
|
+
? ""
|
|
264
|
+
: `, ${q("__event_start")}, ${q("__event_end")}`;
|
|
265
|
+
const eventValues = temporal === undefined
|
|
266
|
+
? ""
|
|
267
|
+
: `, ${ref("edges", "__event_start")}, ${ref("edges", "__event_end")}`;
|
|
268
|
+
const metric = metrics.minimize === "steps"
|
|
269
|
+
? q("steps")
|
|
270
|
+
: metrics.minimize === "weight"
|
|
271
|
+
? q("distance")
|
|
272
|
+
: q("__elapsed");
|
|
273
|
+
// Use native time precision for selection. Collapse equal reported values
|
|
274
|
+
// after conversion to milliseconds, since these are summary rows.
|
|
275
|
+
const reportedElapsed = metrics.elapsedTime
|
|
276
|
+
? `, ${temporal.elapsedMilliseconds(q("__elapsed"))} AS ${q("elapsedTimeMs")}`
|
|
277
|
+
: "";
|
|
278
|
+
const order = metrics.minimize === "steps"
|
|
279
|
+
? [
|
|
280
|
+
q("steps"),
|
|
281
|
+
q("__node_key"),
|
|
282
|
+
...(includeTotal ? [q("total")] : []),
|
|
283
|
+
...(metrics.elapsedTime ? [q("elapsedTimeMs")] : []),
|
|
284
|
+
]
|
|
285
|
+
: metrics.minimize === "weight"
|
|
286
|
+
? [
|
|
287
|
+
q("total"),
|
|
288
|
+
q("__node_key"),
|
|
289
|
+
q("steps"),
|
|
290
|
+
...(metrics.elapsedTime ? [q("elapsedTimeMs")] : []),
|
|
291
|
+
]
|
|
292
|
+
: [
|
|
293
|
+
q("elapsedTimeMs"),
|
|
294
|
+
q("__node_key"),
|
|
295
|
+
q("steps"),
|
|
296
|
+
...(includeTotal ? [q("total")] : []),
|
|
297
|
+
];
|
|
298
|
+
const bounds = summaryBoundsSql(relations.graph_best_metrics, relations.graph_metric_bounds, starts, edges, settings, temporal, searchDirection, metrics.minimize, distanceType, floatingWeight);
|
|
299
|
+
return `WITH RECURSIVE ${settings} AS (
|
|
300
|
+
${temporal === undefined
|
|
301
|
+
? `SELECT 0 AS ${q("__gap")}`
|
|
302
|
+
: `SELECT CAST(? AS HUGEINT) AS ${q("__gap")}`}
|
|
239
303
|
), ${events} AS MATERIALIZED (
|
|
240
304
|
${prepared.edges(direction, [
|
|
241
305
|
`${edgeWeight} AS ${q("__weight")}`,
|
|
242
|
-
...temporal
|
|
306
|
+
...(temporal?.eventSelections("edges") ?? []),
|
|
243
307
|
])}
|
|
244
308
|
), ${edges} AS MATERIALIZED (
|
|
245
|
-
SELECT
|
|
246
|
-
|
|
247
|
-
|
|
309
|
+
SELECT DISTINCT ${q("__from")}, ${q("__to")}, ${q("__from_key")},
|
|
310
|
+
${q("__to_key")}, ${q("__weight")}${temporalColumns}
|
|
311
|
+
FROM ${events} AS ${q("events")}
|
|
312
|
+
${temporal === undefined ? "" : `WHERE ${temporal.eventValidity("events")}`}
|
|
313
|
+
), ${starts} AS (
|
|
248
314
|
SELECT ${q("start")}, ${prepared.key(q("start"))} AS ${q("__key")}
|
|
249
315
|
FROM (VALUES ${prepared.startValues}) AS ${q("start_values")}(${q("start")})
|
|
250
|
-
), ${
|
|
316
|
+
), ${bounds.ctes}, ${summaries}(
|
|
251
317
|
${q("start")}, ${q("node")}, ${q("__start_key")}, ${q("__node_key")},
|
|
252
|
-
${q("
|
|
253
|
-
|
|
254
|
-
) USING KEY(${q("__start_key")}, ${q("__event_id")}, ${q("__anchor")}) AS (
|
|
318
|
+
${q("__visited")}, ${q("steps")}, ${q("distance")}${temporalColumns}${metrics.elapsedTime ? `, ${q("__anchor")}` : ""}
|
|
319
|
+
) AS (
|
|
255
320
|
SELECT ${ref("starts", "start")}, ${ref("edges", "__to")},
|
|
256
321
|
${ref("starts", "__key")}, ${ref("edges", "__to_key")},
|
|
257
|
-
${ref("
|
|
258
|
-
${ref("edges", "
|
|
322
|
+
list_sort(list_distinct([${ref("starts", "__key")}, ${ref("edges", "__to_key")}])),
|
|
323
|
+
CAST(1 AS BIGINT), ${ref("edges", "__weight")}${eventValues}${metrics.elapsedTime ? `, ${firstAnchor}` : ""}
|
|
259
324
|
FROM ${starts} AS ${q("starts")}
|
|
260
325
|
INNER JOIN ${edges} AS ${q("edges")}
|
|
261
326
|
ON ${ref("starts", "__key")} = ${ref("edges", "__from_key")}
|
|
327
|
+
${bounds.join(ref("starts", "__key"))}
|
|
328
|
+
WHERE ${bounds.test("CAST(1 AS BIGINT)", ref("edges", "__weight"), firstAnchor)}
|
|
262
329
|
UNION
|
|
263
330
|
SELECT ${ref("reached", "start")}, ${ref("edges", "__to")},
|
|
264
331
|
${ref("reached", "__start_key")}, ${ref("edges", "__to_key")},
|
|
265
|
-
${ref("
|
|
266
|
-
${ref("
|
|
267
|
-
|
|
268
|
-
FROM ${states} AS ${q("reached")}
|
|
332
|
+
list_sort(list_distinct(list_append(${ref("reached", "__visited")}, ${ref("edges", "__to_key")}))),
|
|
333
|
+
${ref("reached", "steps")} + 1, ${nextDistance}${eventValues}${metrics.elapsedTime ? `, ${ref("reached", "__anchor")}` : ""}
|
|
334
|
+
FROM ${summaries} AS ${q("reached")}
|
|
269
335
|
INNER JOIN ${edges} AS ${q("edges")}
|
|
270
336
|
ON ${ref("reached", "__node_key")} = ${ref("edges", "__from_key")}
|
|
271
337
|
CROSS JOIN ${settings} AS ${q("settings")}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
AND ${ref("
|
|
275
|
-
AND ${
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
${ref("reached", "__start_key")}, ${ref("edges", "__to_key")},
|
|
279
|
-
${ref("edges", "__event_id")}, ${ref("edges", "__event_start")},
|
|
280
|
-
${ref("edges", "__event_end")}, ${reachedAnchor}, ${ref("best", "distance")}
|
|
281
|
-
HAVING ${ref("best", "distance")} IS NULL OR
|
|
282
|
-
MIN(${candidateDistance}) < ${ref("best", "distance")}
|
|
338
|
+
${bounds.join(ref("reached", "__start_key"))}
|
|
339
|
+
WHERE ${bounds.test(`${ref("reached", "steps")} + 1`, nextDistance, ref("reached", "__anchor"))}
|
|
340
|
+
AND ${ref("reached", "__node_key")} <> ${ref("reached", "__start_key")}
|
|
341
|
+
AND (NOT list_contains(${ref("reached", "__visited")}, ${ref("edges", "__to_key")})
|
|
342
|
+
OR ${ref("edges", "__to_key")} = ${ref("reached", "__start_key")})
|
|
343
|
+
${temporal === undefined ? "" : `AND ${temporal.transition("reached", "edges", searchDirection, ref("settings", "__gap"))}`}
|
|
283
344
|
), ${results} AS (
|
|
284
|
-
SELECT
|
|
285
|
-
|
|
345
|
+
SELECT *${metrics.elapsedTime
|
|
346
|
+
? `, ${temporal.journeyElapsed("summaries", ref("summaries", "__anchor"), searchDirection)} AS ${q("__elapsed")}`
|
|
347
|
+
: ""}
|
|
348
|
+
FROM ${summaries} AS ${q("summaries")}
|
|
349
|
+
), ${tied} AS MATERIALIZED (
|
|
350
|
+
SELECT DISTINCT ${q("start")}, ${q("node")}, ${q("__start_key")}, ${q("__node_key")},
|
|
351
|
+
${q("steps")}${includeTotal ? `, ${q("distance")} AS ${q("total")}` : ""}${reportedElapsed}
|
|
352
|
+
FROM ${results}
|
|
353
|
+
QUALIFY ${metric} = MIN(${metric}) OVER (
|
|
354
|
+
PARTITION BY ${q("__start_key")}, ${q("__node_key")}
|
|
355
|
+
)
|
|
286
356
|
)
|
|
287
|
-
SELECT ${q("start")}, ${q("node")}, ${q("
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
)
|
|
294
|
-
|
|
357
|
+
SELECT ${q("start")}, ${q("node")}, ${q("steps")}${includeTotal
|
|
358
|
+
? `, CASE WHEN ${q("total")} IS NULL
|
|
359
|
+
THEN error('distances() total exceeds the supported ${distanceType} range for an optimal route.')
|
|
360
|
+
ELSE ${q("total")} END AS ${q("total")}`
|
|
361
|
+
: ""}${metrics.elapsedTime ? `, ${q("elapsedTimeMs")}` : ""}
|
|
362
|
+
FROM ${tied}
|
|
363
|
+
ORDER BY ${q("__start_key")}, ${order.join(", ")}`;
|
|
364
|
+
}
|
|
365
|
+
// Compute primary optima without enumerating visited sets. Exact additive
|
|
366
|
+
// objectives have optimal prefixes (at the same event boundary in temporal
|
|
367
|
+
// traversal). Floating addition can erase prefix differences, and elapsed
|
|
368
|
+
// optima can depend on a later departure, so those use a per-start upper bound.
|
|
369
|
+
function summaryBoundsSql(best, limits, starts, edges, settings, temporal, direction, minimize, distanceType, floatingWeight) {
|
|
370
|
+
const q = quoteIdentifier_js_1.default;
|
|
371
|
+
const ref = (alias, column) => `${q(alias)}.${q(column)}`;
|
|
372
|
+
const elapsed = minimize === "elapsedTime";
|
|
373
|
+
const globalBound = elapsed || (minimize === "weight" && floatingWeight);
|
|
374
|
+
const boundaries = temporal === undefined
|
|
375
|
+
? []
|
|
376
|
+
: ["__event_start", "__event_end"];
|
|
377
|
+
const keys = [
|
|
378
|
+
"__start_key",
|
|
379
|
+
"__node_key",
|
|
380
|
+
...boundaries,
|
|
381
|
+
...(elapsed ? ["__anchor"] : []),
|
|
382
|
+
];
|
|
383
|
+
const firstAnchor = elapsed
|
|
384
|
+
? temporal.journeyAnchor("edges", direction)
|
|
385
|
+
: undefined;
|
|
386
|
+
const firstKeys = [
|
|
387
|
+
ref("starts", "__key"),
|
|
388
|
+
ref("edges", "__to_key"),
|
|
389
|
+
...boundaries.map((key) => ref("edges", key)),
|
|
390
|
+
...(elapsed ? [firstAnchor] : []),
|
|
391
|
+
];
|
|
392
|
+
const nextKeys = [
|
|
393
|
+
ref("reached", "__start_key"),
|
|
394
|
+
ref("edges", "__to_key"),
|
|
395
|
+
...boundaries.map((key) => ref("edges", key)),
|
|
396
|
+
...(elapsed ? [ref("reached", "__anchor")] : []),
|
|
397
|
+
];
|
|
398
|
+
const firstMetric = minimize === "steps"
|
|
399
|
+
? "CAST(1 AS BIGINT)"
|
|
400
|
+
: elapsed
|
|
401
|
+
? temporal.journeyElapsed("edges", firstAnchor, direction)
|
|
402
|
+
: ref("edges", "__weight");
|
|
403
|
+
const nextMetric = minimize === "steps"
|
|
404
|
+
? `${ref("reached", "__metric")} + 1`
|
|
405
|
+
: elapsed
|
|
406
|
+
? temporal.journeyElapsed("edges", ref("reached", "__anchor"), direction)
|
|
407
|
+
: `CAST(${ref("reached", "__metric")} + ${ref("edges", "__weight")} AS ${distanceType})`;
|
|
408
|
+
const minimum = `MIN(${nextMetric})`;
|
|
409
|
+
const bestMetric = ref("best", "__metric");
|
|
410
|
+
const ctes = `${best}(${keys.map(q).join(", ")}, ${q("__metric")})
|
|
411
|
+
USING KEY(${keys.map(q).join(", ")}) AS (
|
|
412
|
+
SELECT ${firstKeys.join(", ")}, MIN(${firstMetric})
|
|
413
|
+
FROM ${starts} AS ${q("starts")}
|
|
414
|
+
INNER JOIN ${edges} AS ${q("edges")}
|
|
415
|
+
ON ${ref("starts", "__key")} = ${ref("edges", "__from_key")}
|
|
416
|
+
GROUP BY ${firstKeys.join(", ")}
|
|
417
|
+
UNION
|
|
418
|
+
SELECT ${nextKeys.join(", ")}, ${minimum}
|
|
419
|
+
FROM ${best} AS ${q("reached")}
|
|
420
|
+
INNER JOIN ${edges} AS ${q("edges")}
|
|
421
|
+
ON ${ref("reached", "__node_key")} = ${ref("edges", "__from_key")}
|
|
422
|
+
CROSS JOIN ${settings} AS ${q("settings")}
|
|
423
|
+
LEFT JOIN recurring.${best} AS ${q("best")}
|
|
424
|
+
ON ${keys.map((key, i) => `${ref("best", key)} = ${nextKeys[i]}`).join(" AND ")}
|
|
425
|
+
WHERE ${ref("reached", "__node_key")} <> ${ref("reached", "__start_key")}
|
|
426
|
+
${temporal === undefined ? "" : `AND ${temporal.transition("reached", "edges", direction, ref("settings", "__gap"))}`}
|
|
427
|
+
GROUP BY ${nextKeys.join(", ")}, ${bestMetric}
|
|
428
|
+
HAVING ${bestMetric} IS NULL OR ${minimum} < ${bestMetric}
|
|
429
|
+
)${globalBound
|
|
430
|
+
? `, ${limits} AS (
|
|
431
|
+
SELECT ${q("__start_key")}, MAX(${q("__minimum")}) AS ${q("__metric")}
|
|
432
|
+
FROM (
|
|
433
|
+
SELECT ${q("__start_key")}, ${q("__node_key")}, MIN(${q("__metric")}) AS ${q("__minimum")}
|
|
434
|
+
FROM ${best}
|
|
435
|
+
GROUP BY ${q("__start_key")}, ${q("__node_key")}
|
|
436
|
+
) AS ${q("minima")}
|
|
437
|
+
GROUP BY ${q("__start_key")}
|
|
438
|
+
)`
|
|
439
|
+
: ""}`;
|
|
440
|
+
return {
|
|
441
|
+
ctes,
|
|
442
|
+
join: (startKey) => `INNER JOIN ${globalBound ? limits : best} AS ${q("bounds")}
|
|
443
|
+
ON ${ref("bounds", "__start_key")} = ${startKey}
|
|
444
|
+
${globalBound
|
|
445
|
+
? ""
|
|
446
|
+
: `AND ${ref("bounds", "__node_key")} = ${ref("edges", "__to_key")}
|
|
447
|
+
${boundaries.map((key) => `AND ${ref("bounds", key)} = ${ref("edges", key)}`).join(" ")}`}`,
|
|
448
|
+
test: (steps, distance, anchor) => {
|
|
449
|
+
const metric = minimize === "steps"
|
|
450
|
+
? steps
|
|
451
|
+
: elapsed
|
|
452
|
+
? temporal.journeyElapsed("edges", anchor, direction)
|
|
453
|
+
: distance;
|
|
454
|
+
return `${metric} ${globalBound ? "<=" : "="} ${ref("bounds", "__metric")}`;
|
|
455
|
+
},
|
|
456
|
+
};
|
|
295
457
|
}
|
|
@@ -7,7 +7,7 @@ type ShortestPathOptions = GraphTemporalOptions & {
|
|
|
7
7
|
outputTable?: string | boolean;
|
|
8
8
|
weight?: string;
|
|
9
9
|
elapsedTime?: boolean;
|
|
10
|
-
minimize?: "weight" | "elapsedTime";
|
|
10
|
+
minimize?: "steps" | "weight" | "elapsedTime";
|
|
11
11
|
};
|
|
12
12
|
export default function shortestPath(simpleTable: SimpleTable, sourceColumn: string, targetColumn: string, edgeId: string, start: GraphId, end: GraphId, options?: ShortestPathOptions): SimpleTable;
|
|
13
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;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;
|
|
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"}
|
|
@@ -92,7 +92,7 @@ function shortestPath(simpleTable, sourceColumn, targetColumn, edgeId, start, en
|
|
|
92
92
|
? undefined
|
|
93
93
|
: (input) => (0, validateGraphTemporalEvents_js_1.default)(input, temporalOptions, "shortestPath()", parameters),
|
|
94
94
|
values: (schema) => {
|
|
95
|
-
(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);
|
|
96
96
|
const temporal = temporalOptions === undefined
|
|
97
97
|
? undefined
|
|
98
98
|
: (0, prepareGraphTemporalSql_js_1.default)(schema, temporalOptions, "shortestPath()");
|
|
@@ -102,27 +102,28 @@ function shortestPath(simpleTable, sourceColumn, targetColumn, edgeId, start, en
|
|
|
102
102
|
},
|
|
103
103
|
buildSelect: (input, schema) => shortestPathSelect(input, schema, sourceColumn, targetColumn, edgeId, endpoints, direction, options.weight, temporalOptions, metrics),
|
|
104
104
|
outputSchema: (schema) => {
|
|
105
|
-
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);
|
|
106
106
|
if (temporalOptions !== undefined) {
|
|
107
107
|
(0, prepareGraphTemporalSql_js_1.default)(schema, temporalOptions, "shortestPath()");
|
|
108
108
|
}
|
|
109
|
-
return (0, graphRouteResultSchema_js_1.default)(schema, validated.distanceType, "shortestPath()", metrics.elapsedTime);
|
|
109
|
+
return (0, graphRouteResultSchema_js_1.default)(schema, validated.distanceType, "shortestPath()", metrics.elapsedTime, options.weight !== undefined);
|
|
110
110
|
},
|
|
111
111
|
});
|
|
112
112
|
}
|
|
113
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()");
|
|
114
|
+
const route = (0, prepareGraphRouteSql_js_1.prepareGraphRouteSql)(input, schema, source, target, edgeId, routeEndpoints, weight, "shortestPath()", weight !== undefined);
|
|
115
115
|
const prepared = route.traversal;
|
|
116
116
|
if (temporalOptions !== undefined) {
|
|
117
117
|
const temporal = (0, prepareGraphTemporalSql_js_1.default)(schema, temporalOptions, "shortestPath()");
|
|
118
|
-
return temporalShortestPathSelect(route, direction, temporal, metrics);
|
|
118
|
+
return temporalShortestPathSelect(route, direction, temporal, metrics, weight !== undefined);
|
|
119
119
|
}
|
|
120
120
|
const distanceType = route.distanceType;
|
|
121
121
|
// Exact shortest routes have optimal prefixes. Floating addition can erase
|
|
122
122
|
// a prefix-cost difference later, so retain all simple prefixes up to the
|
|
123
123
|
// best complete cost before selecting the full-route ties.
|
|
124
|
-
const floating =
|
|
125
|
-
route.weightType === "DOUBLE";
|
|
124
|
+
const floating = metrics.minimize === "weight" &&
|
|
125
|
+
(route.weightType === "FLOAT" || route.weightType === "DOUBLE");
|
|
126
|
+
const metricType = metrics.minimize === "steps" ? "BIGINT" : distanceType;
|
|
126
127
|
const relations = prepared.relationNames([
|
|
127
128
|
"graph_route_endpoints",
|
|
128
129
|
"graph_edges",
|
|
@@ -148,8 +149,17 @@ function shortestPathSelect(input, schema, source, target, edgeId, routeEndpoint
|
|
|
148
149
|
const edgeToKey = `${q("edges")}.${q("__to_key")}`;
|
|
149
150
|
const edgeTo = `${q("edges")}.${q("__to")}`;
|
|
150
151
|
const edgeCost = `${q("edges")}.${q("__weight")}`;
|
|
152
|
+
const edgeMetric = metrics.minimize === "steps"
|
|
153
|
+
? "CAST(1 AS BIGINT)"
|
|
154
|
+
: edgeCost;
|
|
151
155
|
const routeDistance = `${q("routes")}.${q("distance")}`;
|
|
152
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;
|
|
153
163
|
return `WITH RECURSIVE ${endpointRelation}(
|
|
154
164
|
${q("start")}, ${q("end")}, ${q("__start_key")}, ${q("__end_key")}
|
|
155
165
|
) AS (
|
|
@@ -181,13 +191,13 @@ function shortestPathSelect(input, schema, source, target, edgeId, routeEndpoint
|
|
|
181
191
|
${q("node")}, ${q("__node_key")}, ${q("distance")}
|
|
182
192
|
) USING KEY(${q("__node_key")}) AS (
|
|
183
193
|
SELECT ${q("start")}, ${q("__start_key")},
|
|
184
|
-
CAST(0 AS ${
|
|
194
|
+
CAST(0 AS ${metricType})
|
|
185
195
|
FROM ${endpointRelation}
|
|
186
196
|
INNER JOIN ${toEndRelation}
|
|
187
197
|
ON ${q("__start_key")} = ${q("__key")}
|
|
188
198
|
UNION
|
|
189
199
|
SELECT ${edgeTo}, ${edgeToKey},
|
|
190
|
-
MIN(CAST(${q("reached")}.${q("distance")} + ${
|
|
200
|
+
MIN(CAST(${q("reached")}.${q("distance")} + ${edgeMetric} AS ${metricType}))
|
|
191
201
|
FROM ${distancesRelation} AS ${q("reached")}
|
|
192
202
|
INNER JOIN ${edgesRelation} AS ${q("edges")}
|
|
193
203
|
ON ${q("reached")}.${q("__node_key")} = ${edgeFromKey}
|
|
@@ -197,7 +207,7 @@ function shortestPathSelect(input, schema, source, target, edgeId, routeEndpoint
|
|
|
197
207
|
ON ${edgeToKey} = ${q("best")}.${q("__node_key")}
|
|
198
208
|
GROUP BY ${edgeTo}, ${edgeToKey}, ${q("best")}.${q("distance")}
|
|
199
209
|
HAVING ${q("best")}.${q("distance")} IS NULL
|
|
200
|
-
OR MIN(CAST(${q("reached")}.${q("distance")} + ${
|
|
210
|
+
OR MIN(CAST(${q("reached")}.${q("distance")} + ${edgeMetric} AS ${metricType})) < ${q("best")}.${q("distance")}
|
|
201
211
|
), ${bestTotalRelation} AS (
|
|
202
212
|
SELECT ${q("distances")}.${q("distance")}
|
|
203
213
|
FROM ${distancesRelation} AS ${q("distances")}
|
|
@@ -236,23 +246,23 @@ function shortestPathSelect(input, schema, source, target, edgeId, routeEndpoint
|
|
|
236
246
|
WHERE ${q("routes")}.${q("__node_key")} <> ${q("__end_key")}
|
|
237
247
|
AND NOT list_contains(${q("routes")}.${q("__visited")}, ${edgeToKey})
|
|
238
248
|
AND ${floating
|
|
239
|
-
? `${
|
|
240
|
-
: `${
|
|
249
|
+
? `${candidateMetric} <= ${q("best_total")}.${q("distance")}`
|
|
250
|
+
: `${candidateMetric} = ${q("best_prefix")}.${q("distance")}`}
|
|
241
251
|
), ${shortestRelation} AS (
|
|
242
252
|
SELECT ${q("__edge_keys")}, ${q("steps")}
|
|
243
|
-
FROM ${routesRelation}
|
|
253
|
+
FROM ${routesRelation} AS ${q("routes")}
|
|
244
254
|
CROSS JOIN ${endpointRelation}
|
|
245
255
|
CROSS JOIN ${bestTotalRelation} AS ${q("best_total")}
|
|
246
256
|
WHERE ${q("__node_key")} = ${q("__end_key")}
|
|
247
|
-
AND ${
|
|
257
|
+
AND ${routeMetric} = ${q("best_total")}.${q("distance")}
|
|
248
258
|
), ${rankedRelation} AS (
|
|
249
259
|
SELECT CAST(row_number() OVER (ORDER BY ${q("__edge_keys")}) - 1 AS BIGINT) AS ${q("pathId")},
|
|
250
260
|
${q("steps")}
|
|
251
261
|
FROM ${shortestRelation}
|
|
252
262
|
)
|
|
253
|
-
${(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)}`;
|
|
254
264
|
}
|
|
255
|
-
function temporalShortestPathSelect(route, direction, temporal, metrics) {
|
|
265
|
+
function temporalShortestPathSelect(route, direction, temporal, metrics, includeTotal) {
|
|
256
266
|
const prepared = route.traversal;
|
|
257
267
|
const q = quoteIdentifier_js_1.default;
|
|
258
268
|
const startsSelect = `SELECT ${q("start")},
|
|
@@ -265,7 +275,7 @@ function temporalShortestPathSelect(route, direction, temporal, metrics) {
|
|
|
265
275
|
: (0, buildGraphTemporalCostStateSql_js_1.default)(prepared, startsSelect, direction, temporal, route.distanceType, route.edgeWeight, [
|
|
266
276
|
`${route.typedEdgeId} AS ${q("__edge_id")}`,
|
|
267
277
|
`${route.edgeKey} AS ${q("__edge_key")}`,
|
|
268
|
-
]);
|
|
278
|
+
], metrics.minimize === "steps" ? "steps" : "weight");
|
|
269
279
|
const relations = prepared.relationNames([
|
|
270
280
|
"graph_route_endpoints",
|
|
271
281
|
"graph_nodes",
|
|
@@ -295,15 +305,21 @@ function temporalShortestPathSelect(route, direction, temporal, metrics) {
|
|
|
295
305
|
const routeAnchor = `${q("routes")}.${q("__journey_anchor")}`;
|
|
296
306
|
const firstElapsed = temporal.journeyElapsed("edges", firstAnchor, direction);
|
|
297
307
|
const candidateElapsed = temporal.journeyElapsed("edges", routeAnchor, direction);
|
|
298
|
-
const firstMetric = metrics.minimize === "
|
|
299
|
-
?
|
|
300
|
-
:
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
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;
|
|
307
323
|
const step = (distance, elapsed) => `struct_pack(
|
|
308
324
|
${q("edgeId")} := ${q("edges")}.${q("__edge_id")},
|
|
309
325
|
${q("source")} := ${q("edges")}.${q("__from")},
|
|
@@ -336,7 +352,7 @@ function temporalShortestPathSelect(route, direction, temporal, metrics) {
|
|
|
336
352
|
INNER JOIN ${edgesRelation} AS ${q("edges")}
|
|
337
353
|
ON ${q("reached")}.${q("__key")} = ${edgeToKey}
|
|
338
354
|
), ${bestTotalRelation} AS (
|
|
339
|
-
SELECT MIN(${q("costs")}.${q("distance")}) AS ${q("distance")}
|
|
355
|
+
SELECT MIN(${q("costs")}.${q(metrics.minimize === "steps" ? "steps" : "distance")}) AS ${q("distance")}
|
|
340
356
|
FROM ${costs.costRelation} AS ${q("costs")}
|
|
341
357
|
INNER JOIN ${endpointRelation}
|
|
342
358
|
ON ${q("costs")}.${q("__node_key")} = ${q("__end_key")}
|
|
@@ -391,7 +407,7 @@ function temporalShortestPathSelect(route, direction, temporal, metrics) {
|
|
|
391
407
|
AS ${q("pathId")}, ${q("steps")}
|
|
392
408
|
FROM ${shortestRelation}
|
|
393
409
|
)
|
|
394
|
-
${(0, graphRouteResultSelect_js_1.default)(rankedRelation, route.input, route.edgeIdColumn, false, metrics.elapsedTime)}`;
|
|
410
|
+
${(0, graphRouteResultSelect_js_1.default)(rankedRelation, route.input, route.edgeIdColumn, false, metrics.elapsedTime, includeTotal)}`;
|
|
395
411
|
}
|
|
396
412
|
// At the same physical event a later departure (or earlier incoming arrival)
|
|
397
413
|
// dominates every future elapsed duration. Keyed improvements find the optimum
|