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