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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/esm/class/SimpleTable.d.ts +399 -90
  2. package/esm/class/SimpleTable.d.ts.map +1 -1
  3. package/esm/class/SimpleTable.js +393 -90
  4. package/esm/helpers/buildGraphTemporalCostStateSql.d.ts +1 -1
  5. package/esm/helpers/buildGraphTemporalCostStateSql.d.ts.map +1 -1
  6. package/esm/helpers/buildGraphTemporalCostStateSql.js +23 -9
  7. package/esm/helpers/graphRouteResultSchema.d.ts +1 -1
  8. package/esm/helpers/graphRouteResultSchema.d.ts.map +1 -1
  9. package/esm/helpers/graphRouteResultSchema.js +4 -3
  10. package/esm/helpers/graphRouteResultSelect.d.ts +1 -1
  11. package/esm/helpers/graphRouteResultSelect.d.ts.map +1 -1
  12. package/esm/helpers/graphRouteResultSelect.js +5 -2
  13. package/esm/helpers/prepareGraphMetricOptions.d.ts +14 -0
  14. package/esm/helpers/prepareGraphMetricOptions.d.ts.map +1 -0
  15. package/esm/helpers/prepareGraphMetricOptions.js +41 -0
  16. package/esm/helpers/prepareGraphRouteSql.d.ts +2 -2
  17. package/esm/helpers/prepareGraphRouteSql.d.ts.map +1 -1
  18. package/esm/helpers/prepareGraphRouteSql.js +4 -4
  19. package/esm/helpers/prepareGraphTemporalSql.d.ts +3 -0
  20. package/esm/helpers/prepareGraphTemporalSql.d.ts.map +1 -1
  21. package/esm/helpers/prepareGraphTemporalSql.js +7 -0
  22. package/esm/helpers/validateGraphRouteResultSchema.d.ts +1 -1
  23. package/esm/helpers/validateGraphRouteResultSchema.d.ts.map +1 -1
  24. package/esm/helpers/validateGraphRouteResultSchema.js +3 -1
  25. package/esm/methods/distances.d.ts +2 -0
  26. package/esm/methods/distances.d.ts.map +1 -1
  27. package/esm/methods/distances.js +274 -20
  28. package/esm/methods/findCycles.d.ts +1 -0
  29. package/esm/methods/findCycles.d.ts.map +1 -1
  30. package/esm/methods/findCycles.js +22 -15
  31. package/esm/methods/paths.d.ts +1 -0
  32. package/esm/methods/paths.d.ts.map +1 -1
  33. package/esm/methods/paths.js +21 -14
  34. package/esm/methods/shortestPath.d.ts +2 -0
  35. package/esm/methods/shortestPath.d.ts.map +1 -1
  36. package/esm/methods/shortestPath.js +155 -34
  37. package/llm.md +428 -108
  38. package/llms.txt +1 -1
  39. package/package.json +1 -1
  40. package/script/class/SimpleTable.d.ts +399 -90
  41. package/script/class/SimpleTable.d.ts.map +1 -1
  42. package/script/class/SimpleTable.js +393 -90
  43. package/script/helpers/buildGraphTemporalCostStateSql.d.ts +1 -1
  44. package/script/helpers/buildGraphTemporalCostStateSql.d.ts.map +1 -1
  45. package/script/helpers/buildGraphTemporalCostStateSql.js +23 -9
  46. package/script/helpers/graphRouteResultSchema.d.ts +1 -1
  47. package/script/helpers/graphRouteResultSchema.d.ts.map +1 -1
  48. package/script/helpers/graphRouteResultSchema.js +4 -3
  49. package/script/helpers/graphRouteResultSelect.d.ts +1 -1
  50. package/script/helpers/graphRouteResultSelect.d.ts.map +1 -1
  51. package/script/helpers/graphRouteResultSelect.js +5 -2
  52. package/script/helpers/prepareGraphMetricOptions.d.ts +14 -0
  53. package/script/helpers/prepareGraphMetricOptions.d.ts.map +1 -0
  54. package/script/helpers/prepareGraphMetricOptions.js +44 -0
  55. package/script/helpers/prepareGraphRouteSql.d.ts +2 -2
  56. package/script/helpers/prepareGraphRouteSql.d.ts.map +1 -1
  57. package/script/helpers/prepareGraphRouteSql.js +4 -4
  58. package/script/helpers/prepareGraphTemporalSql.d.ts +3 -0
  59. package/script/helpers/prepareGraphTemporalSql.d.ts.map +1 -1
  60. package/script/helpers/prepareGraphTemporalSql.js +7 -0
  61. package/script/helpers/validateGraphRouteResultSchema.d.ts +1 -1
  62. package/script/helpers/validateGraphRouteResultSchema.d.ts.map +1 -1
  63. package/script/helpers/validateGraphRouteResultSchema.js +3 -1
  64. package/script/methods/distances.d.ts +2 -0
  65. package/script/methods/distances.d.ts.map +1 -1
  66. package/script/methods/distances.js +274 -20
  67. package/script/methods/findCycles.d.ts +1 -0
  68. package/script/methods/findCycles.d.ts.map +1 -1
  69. package/script/methods/findCycles.js +22 -15
  70. package/script/methods/paths.d.ts +1 -0
  71. package/script/methods/paths.d.ts.map +1 -1
  72. package/script/methods/paths.js +21 -14
  73. package/script/methods/shortestPath.d.ts +2 -0
  74. package/script/methods/shortestPath.d.ts.map +1 -1
  75. package/script/methods/shortestPath.js +155 -34
package/llm.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # The Simple Data Analysis (Core) Library
2
2
 
3
3
  - Package: `@nshiab/simple-data-analysis-core`
4
- - Version: `2.1.6`
4
+ - Version: `2.1.8`
5
5
 
6
6
  To install the library with Deno, use:
7
7
 
@@ -3796,40 +3796,24 @@ await table
3796
3796
 
3797
3797
  #### `distances`
3798
3798
 
3799
- Finds the shortest distance from each starting node to every node it can reach
3800
- by following one or more connections. By default, distance is the number of
3801
- connections along the shortest route. Use the `weight` option to find the
3802
- smallest sum of values from a numeric column, such as travel time.
3799
+ Finds the shortest routes from each starting node to every reachable node.
3800
+ Without options, shortest means the fewest connections. Use `weight` to report
3801
+ total cost and `elapsedTime` to report duration including waiting between
3802
+ connections. Use `minimize` to choose which metric defines shortest.
3803
3803
 
3804
- The `direction` option lets you follow connections from source to target, from
3805
- target to source, or in either direction. The result has `start`, `node`, and
3806
- `distance` columns, sorted by `start`, then by increasing `distance`, then by
3807
- `node` to break ties. The closest nodes appear first for each start.
3808
-
3809
- Each row gives the shortest distance from `start` to `node`. Intermediate steps
3810
- along the route are not returned. To get the steps between two different nodes,
3811
- use `shortestPath()` for the shortest routes or `paths()` for all routes without
3812
- repeated nodes.
3813
-
3814
- A starting node appears in its own results only when a self-connection or a
3815
- route leads back to it. Its distance is the shortest actual return route, using
3816
- at least one connection. With `direction: "both"`, this can mean following the
3817
- same connection out and back.
3804
+ Each row contains `start`, `node`, and `steps`, plus `total` when `weight` is
3805
+ supplied and `elapsedTimeMs` when enabled. Routes tied on the chosen metric
3806
+ produce separate rows only when their reported summaries differ; identical
3807
+ summaries appear once.
3818
3808
 
3819
- Unknown starting IDs and starts with no connections to follow produce no rows.
3820
- Empty start arrays and duplicate starting IDs throw an error. Weights must be
3821
- non-null, finite, and non-negative.
3822
-
3823
- Use the `startTimeColumn` or `endTimeColumn` options to follow connections in
3824
- chronological order. The `minGapMs` option sets the minimum gap between
3825
- consecutive connections, in milliseconds. With both time columns, gaps are
3826
- measured from one connection's end to the next connection's start; with one
3827
- column, between their timestamps. The `strictOrdering` option defaults to
3828
- `true`, rejecting zero-duration gaps. These options only work with
3829
- `direction: "outgoing"` or `direction: "incoming"`.
3809
+ Routes cannot repeat nodes except for a final return to the start. A start
3810
+ appears in its own results only through an actual return or self-connection,
3811
+ never with zero steps. With `direction: "both"`, a return can follow the same
3812
+ connection out and back. Use `shortestPath()` to inspect individual connections
3813
+ between two different nodes.
3830
3814
 
3831
- The minimum gap applies only between consecutive connections, not before the
3832
- first connection.
3815
+ Reporting weight or elapsed time can require substantial time and memory on
3816
+ dense networks because many routes may need to be explored.
3833
3817
 
3834
3818
  The next five examples each start with this data:
3835
3819
 
@@ -3845,7 +3829,7 @@ many connections are needed:
3845
3829
  ##### Signature
3846
3830
 
3847
3831
  ```typescript
3848
- distances(sourceColumn: string, targetColumn: string, startNodes: string | number | bigint | (string | number | bigint)[], options?: { direction?: "outgoing" | "incoming" | "both"; endTimeColumn?: string; minGapMs?: number; outputTable?: string | boolean; startTimeColumn?: string; strictOrdering?: boolean; weight?: string }): SimpleTable;
3832
+ distances(sourceColumn: string, targetColumn: string, startNodes: string | number | bigint | (string | number | bigint)[], options?: { direction?: "outgoing" | "incoming" | "both"; elapsedTime?: boolean; endTimeColumn?: string; minGapMs?: number; minimize?: "steps" | "weight" | "elapsedTime"; outputTable?: string | boolean; startTimeColumn?: string; strictOrdering?: boolean; weight?: string }): SimpleTable;
3849
3833
  ```
3850
3834
 
3851
3835
  ##### Parameters
@@ -3854,12 +3838,14 @@ distances(sourceColumn: string, targetColumn: string, startNodes: string | numbe
3854
3838
  node ID.
3855
3839
  - **`targetColumn`**: The name of the column containing each connection's target
3856
3840
  node ID.
3857
- - **`startNodes`**: One starting node ID or an array of distinct starting node
3858
- IDs.
3841
+ - **`startNodes`**: One starting node ID or a non-empty array of distinct IDs.
3842
+ Unknown IDs and starts with no connections to follow produce no rows. Empty
3843
+ arrays and duplicate IDs throw an error.
3859
3844
  - **`options`**: An optional object with direction, time, and result
3860
3845
  configuration.
3861
3846
  - **`options.direction`**: The direction in which to follow connections.
3862
- Defaults to `"outgoing"`.
3847
+ Defaults to `"outgoing"`. Chronological traversal supports only `"outgoing"`
3848
+ and `"incoming"`.
3863
3849
  - **`options.startTimeColumn`**: The name of the column containing each
3864
3850
  connection's start time. Use this or `endTimeColumn` to follow connections in
3865
3851
  chronological order.
@@ -3867,20 +3853,35 @@ distances(sourceColumn: string, targetColumn: string, startNodes: string | numbe
3867
3853
  connection's end time. Use this or `startTimeColumn` to follow connections in
3868
3854
  chronological order.
3869
3855
  - **`options.minGapMs`**: The minimum gap between consecutive connections, in
3870
- milliseconds. Must be a non-negative integer. Defaults to `0`. Requires a time
3871
- column.
3856
+ milliseconds; does not apply before the first connection. With both time
3857
+ columns, measured from one connection's end to the next's start; with one
3858
+ column, between timestamps. Must be a non-negative integer. Defaults to `0`.
3859
+ Requires a time column.
3872
3860
  - **`options.strictOrdering`**: Whether to reject zero-duration gaps between
3873
3861
  consecutive connections (equal timestamps). Defaults to `true`. Requires a
3874
3862
  time column.
3875
3863
  - **`options.weight`**: The name of the numeric column used as the cost of each
3876
- connection. If omitted, each connection costs one.
3864
+ connection. Values must be non-null, finite, and non-negative. Adds `total`,
3865
+ the cumulative sum of those costs. If omitted, no `total` column is generated.
3866
+ - **`options.elapsedTime`**: Whether to add cumulative `elapsedTimeMs` in
3867
+ milliseconds, including connection gaps. Defaults to `false`. Requires both
3868
+ time columns. Optimization compares native timestamp precision before
3869
+ conversion to milliseconds.
3870
+ - **`options.minimize`**: The metric used to select optimal routes: `"steps"`,
3871
+ `"weight"`, or `"elapsedTime"`. Defaults to `"weight"` when only `weight` is
3872
+ enabled, `"elapsedTime"` when only elapsed-time reporting is enabled, and
3873
+ `"steps"` when neither is enabled. Required when both are enabled. `"steps"`
3874
+ is always available; the other metrics require their corresponding options.
3877
3875
  - **`options.outputTable`**: If `true`, stores the result in a new table with a
3878
3876
  generated name. If a string, uses it as the new table's name. If `false` or
3879
3877
  omitted, overwrites the current table. Defaults to `false`.
3880
3878
 
3881
3879
  ##### Returns
3882
3880
 
3883
- The result table, so methods can be chained.
3881
+ The table of distinct optimal summaries with `start`, `node`, and `steps`, plus
3882
+ `total` when `weight` is supplied and `elapsedTimeMs` when enabled, so methods
3883
+ can be chained. Sorted by start, the chosen metric, node, then remaining metrics
3884
+ in the order steps, total, elapsed time.
3884
3885
 
3885
3886
  ##### Examples
3886
3887
 
@@ -3890,12 +3891,12 @@ await connections
3890
3891
  .log();
3891
3892
  ```
3892
3893
 
3893
- | start | node | distance |
3894
- | ----- | ---- | -------: |
3895
- | A | B | 1 |
3896
- | A | C | 2 |
3894
+ | start | node | steps |
3895
+ | ----- | ---- | ----: |
3896
+ | A | B | 1 |
3897
+ | A | C | 2 |
3897
3898
 
3898
- With `weight: "minutes"`, distance is the shortest total travel time:
3899
+ With `weight: "minutes"`, `total` is the sum of travel times:
3899
3900
 
3900
3901
  ```ts
3901
3902
  await connections
@@ -3903,10 +3904,10 @@ await connections
3903
3904
  .log();
3904
3905
  ```
3905
3906
 
3906
- | start | node | distance |
3907
- | ----- | ---- | -------: |
3908
- | A | B | 4 |
3909
- | A | C | 5 |
3907
+ | start | node | steps | total |
3908
+ | ----- | ---- | ----: | ----: |
3909
+ | A | B | 1 | 4 |
3910
+ | A | C | 2 | 5 |
3910
3911
 
3911
3912
  With `direction: "incoming"`, connections are followed from target to source.
3912
3913
  Starting at C:
@@ -3917,11 +3918,11 @@ await connections
3917
3918
  .log();
3918
3919
  ```
3919
3920
 
3920
- | start | node | distance |
3921
- | ----- | ---- | -------: |
3922
- | C | B | 1 |
3923
- | C | A | 2 |
3924
- | C | D | 2 |
3921
+ | start | node | steps |
3922
+ | ----- | ---- | ----: |
3923
+ | C | B | 1 |
3924
+ | C | A | 2 |
3925
+ | C | D | 2 |
3925
3926
 
3926
3927
  With `direction: "both"`, connections can be followed in either direction:
3927
3928
 
@@ -3931,14 +3932,14 @@ await connections
3931
3932
  .log();
3932
3933
  ```
3933
3934
 
3934
- | start | node | distance |
3935
- | ----- | ---- | -------: |
3936
- | A | B | 1 |
3937
- | A | A | 2 |
3938
- | A | C | 2 |
3939
- | A | D | 2 |
3935
+ | start | node | steps |
3936
+ | ----- | ---- | ----: |
3937
+ | A | B | 1 |
3938
+ | A | A | 2 |
3939
+ | A | C | 2 |
3940
+ | A | D | 2 |
3940
3941
 
3941
- A appears at distance 2 because A → B → A follows a connection out and back.
3942
+ A appears with 2 steps because A → B → A follows a connection out and back.
3942
3943
 
3943
3944
  An array of starting nodes gives separate distances for each start:
3944
3945
 
@@ -3950,12 +3951,12 @@ await connections
3950
3951
  .log();
3951
3952
  ```
3952
3953
 
3953
- | start | node | distance |
3954
- | ----- | ---- | -------: |
3955
- | A | B | 4 |
3956
- | A | C | 5 |
3957
- | D | B | 2 |
3958
- | D | C | 3 |
3954
+ | start | node | steps | total |
3955
+ | ----- | ---- | ----: | ----: |
3956
+ | A | B | 1 | 4 |
3957
+ | A | C | 2 | 5 |
3958
+ | D | B | 1 | 2 |
3959
+ | D | C | 2 | 3 |
3959
3960
 
3960
3961
  A direct self-connection and a longer return route can both lead back to the
3961
3962
  start. This example uses different data:
@@ -3975,14 +3976,14 @@ await connections
3975
3976
  .log();
3976
3977
  ```
3977
3978
 
3978
- | start | node | distance |
3979
- | ----- | ---- | -------: |
3980
- | A | B | 2 |
3981
- | A | A | 5 |
3979
+ | start | node | steps | total |
3980
+ | ----- | ---- | ----: | ----: |
3981
+ | A | B | 1 | 2 |
3982
+ | A | A | 2 | 5 |
3982
3983
 
3983
3984
  Chronological options can rule out a cheaper sequence that departs too early.
3984
- For this example, F2 leaves before F1 arrives, while F3 meets the one-hour
3985
- minimum exactly:
3985
+ Here, B → C leaves before A → B arrives, while B → D meets the one-hour minimum
3986
+ exactly:
3986
3987
 
3987
3988
  | origin | destination | departureTime | arrivalTime | minutes |
3988
3989
  | ------ | ----------- | ---------------- | ---------------- | ------: |
@@ -4001,10 +4002,127 @@ await scheduledFlights
4001
4002
  .log();
4002
4003
  ```
4003
4004
 
4004
- | start | node | distance |
4005
- | ----- | ---- | -------: |
4006
- | A | B | 2 |
4007
- | A | D | 5 |
4005
+ | start | node | steps | total |
4006
+ | ----- | ---- | ----: | ----: |
4007
+ | A | B | 1 | 2 |
4008
+ | A | D | 2 | 5 |
4009
+
4010
+ For the next four examples, `flights` contains these rows, with departure and
4011
+ arrival stored as timestamps on the same day:
4012
+
4013
+ | flightId | origin | destination | departure | arrival | price |
4014
+ | -------- | ------ | ----------- | ---------------- | ---------------- | ----: |
4015
+ | F1 | A | B | 2025-01-01 09:00 | 2025-01-01 10:00 | 40 |
4016
+ | F2 | B | C | 2025-01-01 12:00 | 2025-01-01 13:00 | 40 |
4017
+ | F3 | A | C | 2025-01-01 09:00 | 2025-01-01 12:00 | 120 |
4018
+
4019
+ Find the fastest journey from A to each reachable destination, including
4020
+ layovers:
4021
+
4022
+ ```ts
4023
+ await flights
4024
+ .distances("origin", "destination", "A", {
4025
+ startTimeColumn: "departure",
4026
+ endTimeColumn: "arrival",
4027
+ minGapMs: 60 * 60 * 1000,
4028
+ elapsedTime: true,
4029
+ })
4030
+ .log();
4031
+ ```
4032
+
4033
+ | start | node | steps | elapsedTimeMs |
4034
+ | ----- | ---- | ----: | ------------: |
4035
+ | A | B | 1 | 3600000 |
4036
+ | A | C | 1 | 10800000 |
4037
+
4038
+ The direct flight to C takes three hours (10,800,000 milliseconds). The route
4039
+ through B takes four hours including its layover.
4040
+
4041
+ Find the fastest journeys from A while also adding up their prices:
4042
+
4043
+ ```ts
4044
+ await flights
4045
+ .distances("origin", "destination", "A", {
4046
+ startTimeColumn: "departure",
4047
+ endTimeColumn: "arrival",
4048
+ minGapMs: 60 * 60 * 1000,
4049
+ elapsedTime: true,
4050
+ weight: "price",
4051
+ minimize: "elapsedTime",
4052
+ })
4053
+ .log();
4054
+ ```
4055
+
4056
+ | start | node | steps | total | elapsedTimeMs |
4057
+ | ----- | ---- | ----: | ----: | ------------: |
4058
+ | A | B | 1 | 40 | 3600000 |
4059
+ | A | C | 1 | 120 | 10800000 |
4060
+
4061
+ Use `minimize: "weight"` to find the cheapest journeys instead. The selected
4062
+ route to C now takes two steps through B and costs 80:
4063
+
4064
+ ```ts
4065
+ await flights
4066
+ .distances("origin", "destination", "A", {
4067
+ startTimeColumn: "departure",
4068
+ endTimeColumn: "arrival",
4069
+ minGapMs: 60 * 60 * 1000,
4070
+ elapsedTime: true,
4071
+ weight: "price",
4072
+ minimize: "weight",
4073
+ })
4074
+ .log();
4075
+ ```
4076
+
4077
+ | start | node | steps | total | elapsedTimeMs |
4078
+ | ----- | ---- | ----: | ----: | ------------: |
4079
+ | A | B | 1 | 40 | 3600000 |
4080
+ | A | C | 2 | 80 | 14400000 |
4081
+
4082
+ Choose the fewest connections while still reporting price and elapsed time. The
4083
+ direct flight to C has one step, even though it costs more:
4084
+
4085
+ ```ts
4086
+ await flights
4087
+ .distances("origin", "destination", "A", {
4088
+ startTimeColumn: "departure",
4089
+ endTimeColumn: "arrival",
4090
+ minGapMs: 60 * 60 * 1000,
4091
+ elapsedTime: true,
4092
+ weight: "price",
4093
+ minimize: "steps",
4094
+ })
4095
+ .log();
4096
+ ```
4097
+
4098
+ | start | node | steps | total | elapsedTimeMs |
4099
+ | ----- | ---- | ----: | ----: | ------------: |
4100
+ | A | B | 1 | 40 | 3600000 |
4101
+ | A | C | 1 | 120 | 10800000 |
4102
+
4103
+ When two routes to C both take two steps but have different prices, minimizing
4104
+ steps while reporting price returns both summaries:
4105
+
4106
+ ```ts
4107
+ await connections
4108
+ .loadArray([
4109
+ { origin: "A", destination: "B", price: 20 },
4110
+ { origin: "B", destination: "C", price: 30 },
4111
+ { origin: "A", destination: "D", price: 40 },
4112
+ { origin: "D", destination: "C", price: 40 },
4113
+ ])
4114
+ .distances("origin", "destination", "A", {
4115
+ weight: "price",
4116
+ minimize: "steps",
4117
+ })
4118
+ .filter("node = 'C'")
4119
+ .log();
4120
+ ```
4121
+
4122
+ | start | node | steps | total |
4123
+ | ----- | ---- | ----: | ----: |
4124
+ | A | C | 2 | 50 |
4125
+ | A | C | 2 | 80 |
4008
4126
 
4009
4127
  #### `shortestPath`
4010
4128
 
@@ -4022,15 +4140,17 @@ Each connection needs a unique, non-null ID. Pass the name of the column
4022
4140
  containing these IDs as the `edgeId` argument. If your table is missing an ID
4023
4141
  column, you can easily create one with `addId()`.
4024
4142
 
4025
- The result starts with the `pathId`, `step`, `weight`, and `total` columns,
4026
- followed by all original columns. Steps start at one and follow the search
4027
- direction. The `weight` column is the connection cost, and `total` is the sum of
4028
- those costs up to and including the current step. Without the `weight` option,
4029
- each connection costs one.
4143
+ The result starts with `pathId`, `step`, and `weight`, followed by the optional
4144
+ metric columns and all original columns. Steps start at one and follow the
4145
+ search direction. The `weight` column is the connection cost, or one when no
4146
+ `weight` option is supplied. Supplying `weight` also adds `total`, the sum of
4147
+ those costs up to and including the current step. Enabling `elapsedTime` adds
4148
+ cumulative `elapsedTimeMs`, including gaps.
4030
4149
 
4031
- If an input column is already named `pathId`, `step`, `weight`, or `total`
4032
- (regardless of capitalization), the method throws an error. Rename it with
4033
- `renameColumns()` first.
4150
+ If an input column conflicts with a generated result column (regardless of
4151
+ capitalization), the method throws an error. Rename it with `renameColumns()`
4152
+ first. `total` is reserved only when `weight` is supplied, and `elapsedTimeMs`
4153
+ only when elapsed-time reporting is enabled.
4034
4154
 
4035
4155
  Each row is one connection along a route.
4036
4156
 
@@ -4045,6 +4165,16 @@ column, between their timestamps. The `strictOrdering` option defaults to
4045
4165
  The minimum gap applies only between consecutive connections, not before the
4046
4166
  first connection.
4047
4167
 
4168
+ Use `minimize: "steps"` to choose the fewest connections even when weight or
4169
+ elapsed-time reporting is enabled. Without an explicit `minimize`, the method
4170
+ minimizes weight when only `weight` is enabled, elapsed time when only
4171
+ `elapsedTime` is enabled, and steps when neither is enabled. With both enabled,
4172
+ specify `"steps"`, `"weight"`, or `"elapsedTime"` explicitly. Elapsed-time
4173
+ reporting requires both time columns.
4174
+
4175
+ All routes tied on the selected metric are returned, even if their other metrics
4176
+ differ. Each route reports its own weights and elapsed times.
4177
+
4048
4178
  Routes are numbered from zero by comparing their sequences of connection IDs,
4049
4179
  element by element. Rows are sorted by `pathId`, then `step`.
4050
4180
 
@@ -4071,7 +4201,7 @@ connections. Two routes from A to E tie at three connections each:
4071
4201
  ##### Signature
4072
4202
 
4073
4203
  ```typescript
4074
- shortestPath(sourceColumn: string, targetColumn: string, edgeId: string, start: string | number | bigint, end: string | number | bigint, options?: { direction?: "outgoing" | "incoming" | "both"; endTimeColumn?: string; minGapMs?: number; outputTable?: string | boolean; startTimeColumn?: string; strictOrdering?: boolean; weight?: string }): SimpleTable;
4204
+ shortestPath(sourceColumn: string, targetColumn: string, edgeId: string, start: string | number | bigint, end: string | number | bigint, options?: { direction?: "outgoing" | "incoming" | "both"; elapsedTime?: boolean; endTimeColumn?: string; minGapMs?: number; minimize?: "steps" | "weight" | "elapsedTime"; outputTable?: string | boolean; startTimeColumn?: string; strictOrdering?: boolean; weight?: string }): SimpleTable;
4075
4205
  ```
4076
4206
 
4077
4207
  ##### Parameters
@@ -4101,14 +4231,24 @@ shortestPath(sourceColumn: string, targetColumn: string, edgeId: string, start:
4101
4231
  consecutive connections (equal timestamps). Defaults to `true`. Requires a
4102
4232
  time column.
4103
4233
  - **`options.weight`**: The name of the numeric column used as the cost of each
4104
- connection. If omitted, each connection costs one.
4234
+ connection. Adds `total`, the cumulative sum of those costs. If omitted, no
4235
+ `total` column is generated.
4236
+ - **`options.elapsedTime`**: Whether to add cumulative `elapsedTimeMs` in
4237
+ milliseconds, including connection gaps. Defaults to `false`. Requires both
4238
+ time columns.
4239
+ - **`options.minimize`**: The metric used to select optimal routes: `"steps"`,
4240
+ `"weight"`, or `"elapsedTime"`. Defaults to `"weight"` when only `weight` is
4241
+ enabled, `"elapsedTime"` when only elapsed-time reporting is enabled, and
4242
+ `"steps"` when neither is enabled. Required when both are enabled. `"steps"`
4243
+ is always available; the other metrics require their corresponding options.
4105
4244
  - **`options.outputTable`**: If `true`, stores the result in a new table with a
4106
4245
  generated name. If a string, uses it as the new table's name. If `false` or
4107
4246
  omitted, overwrites the current table. Defaults to `false`.
4108
4247
 
4109
4248
  ##### Returns
4110
4249
 
4111
- The result table, so methods can be chained.
4250
+ The table of route connections with optional cumulative `total` and
4251
+ `elapsedTimeMs` metrics, so methods can be chained.
4112
4252
 
4113
4253
  ##### Examples
4114
4254
 
@@ -4118,14 +4258,14 @@ await connections
4118
4258
  .log();
4119
4259
  ```
4120
4260
 
4121
- | pathId | step | weight | total | edgeId | source | target |
4122
- | -----: | ---: | -----: | ----: | ------ | ------ | ------ |
4123
- | 0 | 1 | 1 | 1 | E1 | A | B |
4124
- | 0 | 2 | 1 | 2 | E3 | B | D |
4125
- | 0 | 3 | 1 | 3 | E5 | D | E |
4126
- | 1 | 1 | 1 | 1 | E2 | A | C |
4127
- | 1 | 2 | 1 | 2 | E4 | C | D |
4128
- | 1 | 3 | 1 | 3 | E5 | D | E |
4261
+ | pathId | step | weight | edgeId | source | target |
4262
+ | -----: | ---: | -----: | ------ | ------ | ------ |
4263
+ | 0 | 1 | 1 | E1 | A | B |
4264
+ | 0 | 2 | 1 | E3 | B | D |
4265
+ | 0 | 3 | 1 | E5 | D | E |
4266
+ | 1 | 1 | 1 | E2 | A | C |
4267
+ | 1 | 2 | 1 | E4 | C | D |
4268
+ | 1 | 3 | 1 | E5 | D | E |
4129
4269
 
4130
4270
  For an input without connection IDs, use `addId()` first:
4131
4271
 
@@ -4143,10 +4283,10 @@ await unnumberedConnections
4143
4283
  .log();
4144
4284
  ```
4145
4285
 
4146
- | pathId | step | weight | total | source | target | edgeId |
4147
- | -----: | ---: | -----: | ----: | ------ | ------ | ------ |
4148
- | 0 | 1 | 1 | 1 | A | B | edge-0 |
4149
- | 0 | 2 | 1 | 2 | B | E | edge-1 |
4286
+ | pathId | step | weight | source | target | edgeId |
4287
+ | -----: | ---: | -----: | ------ | ------ | ------ |
4288
+ | 0 | 1 | 1 | A | B | edge-0 |
4289
+ | 0 | 2 | 1 | B | E | edge-1 |
4150
4290
 
4151
4291
  The next two examples each start with these flights. The direct flight takes ten
4152
4292
  minutes; the route through B and D takes three:
@@ -4183,9 +4323,9 @@ await flights
4183
4323
  .log();
4184
4324
  ```
4185
4325
 
4186
- | pathId | step | weight | total | flightId | origin | destination | minutes |
4187
- | -----: | ---: | -----: | ----: | -------- | ------ | ----------- | ------: |
4188
- | 0 | 1 | 1 | 1 | F1 | A | E | 10 |
4326
+ | pathId | step | weight | flightId | origin | destination | minutes |
4327
+ | -----: | ---: | -----: | -------- | ------ | ----------- | ------: |
4328
+ | 0 | 1 | 1 | F1 | A | E | 10 |
4189
4329
 
4190
4330
  With time options, the cheaper route through B is ruled out because F2 leaves
4191
4331
  before F1 arrives. The route through C meets the one-hour minimum gap:
@@ -4228,9 +4368,84 @@ await reverseExample
4228
4368
  .log();
4229
4369
  ```
4230
4370
 
4231
- | pathId | step | weight | total | edgeId | source | target |
4232
- | -----: | ---: | -----: | ----: | ------ | ------ | ------ |
4233
- | 0 | 1 | 1 | 1 | F1 | A | B |
4371
+ | pathId | step | weight | edgeId | source | target |
4372
+ | -----: | ---: | -----: | ------ | ------ | ------ |
4373
+ | 0 | 1 | 1 | F1 | A | B |
4374
+
4375
+ For the next three examples, `flights` contains these rows, with departure and
4376
+ arrival stored as timestamps on the same day:
4377
+
4378
+ | flightId | origin | destination | departure | arrival | price |
4379
+ | -------- | ------ | ----------- | ---------------- | ---------------- | ----: |
4380
+ | F1 | A | B | 2025-01-01 09:00 | 2025-01-01 10:00 | 40 |
4381
+ | F2 | B | C | 2025-01-01 12:00 | 2025-01-01 13:00 | 40 |
4382
+ | F3 | A | C | 2025-01-01 09:00 | 2025-01-01 12:00 | 120 |
4383
+ | F4 | A | D | 2025-01-01 09:00 | 2025-01-01 09:30 | 60 |
4384
+ | F5 | D | C | 2025-01-01 10:30 | 2025-01-01 11:00 | 80 |
4385
+
4386
+ Find the fastest journey from A to C, including layovers:
4387
+
4388
+ ```ts
4389
+ await flights
4390
+ .shortestPath("origin", "destination", "flightId", "A", "C", {
4391
+ startTimeColumn: "departure",
4392
+ endTimeColumn: "arrival",
4393
+ minGapMs: 60 * 60 * 1000,
4394
+ elapsedTime: true,
4395
+ })
4396
+ .log();
4397
+ ```
4398
+
4399
+ | pathId | step | weight | elapsedTimeMs | flightId | origin | destination | departure | arrival | price |
4400
+ | -----: | ---: | -----: | ------------: | -------- | ------ | ----------- | ------------------- | ------------------- | ----: |
4401
+ | 0 | 1 | 1 | 1800000 | F4 | A | D | 2025-01-01 09:00:00 | 2025-01-01 09:30:00 | 60 |
4402
+ | 0 | 2 | 1 | 7200000 | F5 | D | C | 2025-01-01 10:30:00 | 2025-01-01 11:00:00 | 80 |
4403
+
4404
+ The route through D takes two hours (7,200,000 milliseconds), including a
4405
+ one-hour layover. The direct flight takes three hours.
4406
+
4407
+ Find the fastest journey while also adding up its price:
4408
+
4409
+ ```ts
4410
+ await flights
4411
+ .shortestPath("origin", "destination", "flightId", "A", "C", {
4412
+ startTimeColumn: "departure",
4413
+ endTimeColumn: "arrival",
4414
+ minGapMs: 60 * 60 * 1000,
4415
+ elapsedTime: true,
4416
+ weight: "price",
4417
+ minimize: "elapsedTime",
4418
+ })
4419
+ .log();
4420
+ ```
4421
+
4422
+ | pathId | step | weight | total | elapsedTimeMs | flightId | origin | destination | departure | arrival | price |
4423
+ | -----: | ---: | -----: | ----: | ------------: | -------- | ------ | ----------- | ------------------- | ------------------- | ----: |
4424
+ | 0 | 1 | 60 | 60 | 1800000 | F4 | A | D | 2025-01-01 09:00:00 | 2025-01-01 09:30:00 | 60 |
4425
+ | 0 | 2 | 80 | 140 | 7200000 | F5 | D | C | 2025-01-01 10:30:00 | 2025-01-01 11:00:00 | 80 |
4426
+
4427
+ Use `minimize: "weight"` to find the cheapest journey instead. All ties on the
4428
+ chosen metric are returned.
4429
+
4430
+ Choose the fewest flights while still reporting price and elapsed time. The
4431
+ direct flight takes one step, although the route through D is faster:
4432
+
4433
+ ```ts
4434
+ await flights
4435
+ .shortestPath("origin", "destination", "flightId", "A", "C", {
4436
+ startTimeColumn: "departure",
4437
+ endTimeColumn: "arrival",
4438
+ minGapMs: 60 * 60 * 1000,
4439
+ elapsedTime: true,
4440
+ weight: "price",
4441
+ minimize: "steps",
4442
+ })
4443
+ .log();
4444
+ ```
4445
+
4446
+ | pathId | step | weight | total | elapsedTimeMs | flightId | origin | destination | departure | arrival | price |
4447
+ | -----: | ---: | -----: | ----: | ------------: | -------- | ------ | ----------- | ------------------- | ------------------- | ----: |
4448
+ | 0 | 1 | 120 | 120 | 10800000 | F3 | A | C | 2025-01-01 09:00:00 | 2025-01-01 12:00:00 | 120 |
4234
4449
 
4235
4450
  #### `paths`
4236
4451
 
@@ -4292,7 +4507,7 @@ to D are returned:
4292
4507
  ##### Signature
4293
4508
 
4294
4509
  ```typescript
4295
- paths(sourceColumn: string, targetColumn: string, edgeId: string, start: string | number | bigint, end: string | number | bigint, options?: { direction?: "outgoing" | "incoming" | "both"; endTimeColumn?: string; minGapMs?: number; outputTable?: string | boolean; startTimeColumn?: string; strictOrdering?: boolean; weight?: string }): SimpleTable;
4510
+ paths(sourceColumn: string, targetColumn: string, edgeId: string, start: string | number | bigint, end: string | number | bigint, options?: { direction?: "outgoing" | "incoming" | "both"; elapsedTime?: boolean; endTimeColumn?: string; minGapMs?: number; outputTable?: string | boolean; startTimeColumn?: string; strictOrdering?: boolean; weight?: string }): SimpleTable;
4296
4511
  ```
4297
4512
 
4298
4513
  ##### Parameters
@@ -4323,6 +4538,9 @@ paths(sourceColumn: string, targetColumn: string, edgeId: string, start: string
4323
4538
  time column.
4324
4539
  - **`options.weight`**: The name of the numeric column used as the cost of each
4325
4540
  connection. If omitted, each connection costs one.
4541
+ - **`options.elapsedTime`**: Whether to add cumulative `elapsedTimeMs` in
4542
+ milliseconds, including connection gaps. Defaults to `false`. Requires both
4543
+ time columns.
4326
4544
  - **`options.outputTable`**: If `true`, stores the result in a new table with a
4327
4545
  generated name. If a string, uses it as the new table's name. If `false` or
4328
4546
  omitted, overwrites the current table. Defaults to `false`.
@@ -4473,6 +4691,57 @@ await scheduledFlights
4473
4691
  | 0 | 1 | 3 | 3 | F3 | B | D | 2025-01-01 11:00:00 | 2025-01-01 12:00:00 | 3 |
4474
4692
  | 0 | 2 | 2 | 5 | F1 | A | B | 2025-01-01 08:00:00 | 2025-01-01 10:00:00 | 2 |
4475
4693
 
4694
+ For the next two examples, `flights` contains these rows, with departure and
4695
+ arrival stored as timestamps on the same day:
4696
+
4697
+ | flightId | origin | destination | departure | arrival | price |
4698
+ | -------- | ------ | ----------- | ---------------- | ---------------- | ----: |
4699
+ | F1 | A | B | 2025-01-01 09:00 | 2025-01-01 10:00 | 40 |
4700
+ | F2 | B | C | 2025-01-01 12:00 | 2025-01-01 13:00 | 40 |
4701
+ | F3 | A | C | 2025-01-01 09:00 | 2025-01-01 12:00 | 120 |
4702
+
4703
+ Report elapsed time for every path, including layovers:
4704
+
4705
+ ```ts
4706
+ await flights
4707
+ .paths("origin", "destination", "flightId", "A", "C", {
4708
+ startTimeColumn: "departure",
4709
+ endTimeColumn: "arrival",
4710
+ minGapMs: 60 * 60 * 1000,
4711
+ elapsedTime: true,
4712
+ })
4713
+ .log();
4714
+ ```
4715
+
4716
+ | pathId | step | weight | total | elapsedTimeMs | flightId | origin | destination | departure | arrival | price |
4717
+ | -----: | ---: | -----: | ----: | ------------: | -------- | ------ | ----------- | ------------------- | ------------------- | ----: |
4718
+ | 0 | 1 | 1 | 1 | 3600000 | F1 | A | B | 2025-01-01 09:00:00 | 2025-01-01 10:00:00 | 40 |
4719
+ | 0 | 2 | 1 | 2 | 14400000 | F2 | B | C | 2025-01-01 12:00:00 | 2025-01-01 13:00:00 | 40 |
4720
+ | 1 | 1 | 1 | 1 | 10800000 | F3 | A | C | 2025-01-01 09:00:00 | 2025-01-01 12:00:00 | 120 |
4721
+
4722
+ The route through B takes four hours (14,400,000 milliseconds), including its
4723
+ two-hour layover. The direct flight takes three hours.
4724
+
4725
+ Add up prices while continuing to report elapsed time for every path:
4726
+
4727
+ ```ts
4728
+ await flights
4729
+ .paths("origin", "destination", "flightId", "A", "C", {
4730
+ startTimeColumn: "departure",
4731
+ endTimeColumn: "arrival",
4732
+ minGapMs: 60 * 60 * 1000,
4733
+ elapsedTime: true,
4734
+ weight: "price",
4735
+ })
4736
+ .log();
4737
+ ```
4738
+
4739
+ | pathId | step | weight | total | elapsedTimeMs | flightId | origin | destination | departure | arrival | price |
4740
+ | -----: | ---: | -----: | ----: | ------------: | -------- | ------ | ----------- | ------------------- | ------------------- | ----: |
4741
+ | 0 | 1 | 40 | 40 | 3600000 | F1 | A | B | 2025-01-01 09:00:00 | 2025-01-01 10:00:00 | 40 |
4742
+ | 0 | 2 | 40 | 80 | 14400000 | F2 | B | C | 2025-01-01 12:00:00 | 2025-01-01 13:00:00 | 40 |
4743
+ | 1 | 1 | 120 | 120 | 10800000 | F3 | A | C | 2025-01-01 09:00:00 | 2025-01-01 12:00:00 | 120 |
4744
+
4476
4745
  #### `findCycles`
4477
4746
 
4478
4747
  Finds all cycles starting and ending at each node in `startNodes`. Pass one node
@@ -4546,7 +4815,7 @@ By default, connections are followed from source to target:
4546
4815
  ##### Signature
4547
4816
 
4548
4817
  ```typescript
4549
- findCycles(sourceColumn: string, targetColumn: string, edgeId: string, startNodes: string | number | bigint | (string | number | bigint)[], options?: { direction?: "outgoing" | "incoming" | "both"; endTimeColumn?: string; minGapMs?: number; outputTable?: string | boolean; startTimeColumn?: string; strictOrdering?: boolean; weight?: string }): SimpleTable;
4818
+ findCycles(sourceColumn: string, targetColumn: string, edgeId: string, startNodes: string | number | bigint | (string | number | bigint)[], options?: { direction?: "outgoing" | "incoming" | "both"; elapsedTime?: boolean; endTimeColumn?: string; minGapMs?: number; outputTable?: string | boolean; startTimeColumn?: string; strictOrdering?: boolean; weight?: string }): SimpleTable;
4550
4819
  ```
4551
4820
 
4552
4821
  ##### Parameters
@@ -4577,6 +4846,9 @@ findCycles(sourceColumn: string, targetColumn: string, edgeId: string, startNode
4577
4846
  time column.
4578
4847
  - **`options.weight`**: The name of the numeric column used as the cost of each
4579
4848
  connection. If omitted, each connection costs one.
4849
+ - **`options.elapsedTime`**: Whether to add cumulative `elapsedTimeMs` in
4850
+ milliseconds, including connection gaps. Defaults to `false`. Requires both
4851
+ time columns.
4580
4852
  - **`options.outputTable`**: If `true`, stores the result in a new table with a
4581
4853
  generated name. If a string, uses it as the new table's name. If `false` or
4582
4854
  omitted, overwrites the current table. Defaults to `false`.
@@ -4755,6 +5027,54 @@ await timedFlights
4755
5027
  | B | 0 | 2 | 1 | 2 | F2 | C | A | 2025-01-01 10:00:00 |
4756
5028
  | B | 0 | 3 | 1 | 3 | F1 | B | C | 2025-01-01 09:00:00 |
4757
5029
 
5030
+ For the next two examples, `flights` contains these rows, with departure and
5031
+ arrival stored as timestamps on the same day:
5032
+
5033
+ | flightId | origin | destination | departure | arrival | price |
5034
+ | -------- | ------ | ----------- | ---------------- | ---------------- | ----: |
5035
+ | F1 | A | B | 2025-01-01 09:00 | 2025-01-01 10:00 | 40 |
5036
+ | F2 | B | A | 2025-01-01 12:00 | 2025-01-01 13:00 | 60 |
5037
+
5038
+ Report elapsed time for every cycle, including layovers:
5039
+
5040
+ ```ts
5041
+ await flights
5042
+ .findCycles("origin", "destination", "flightId", "A", {
5043
+ startTimeColumn: "departure",
5044
+ endTimeColumn: "arrival",
5045
+ minGapMs: 60 * 60 * 1000,
5046
+ elapsedTime: true,
5047
+ })
5048
+ .log();
5049
+ ```
5050
+
5051
+ | start | pathId | step | weight | total | elapsedTimeMs | flightId | origin | destination | departure | arrival | price |
5052
+ | ----- | -----: | ---: | -----: | ----: | ------------: | -------- | ------ | ----------- | ------------------- | ------------------- | ----: |
5053
+ | A | 0 | 1 | 1 | 1 | 3600000 | F1 | A | B | 2025-01-01 09:00:00 | 2025-01-01 10:00:00 | 40 |
5054
+ | A | 0 | 2 | 1 | 2 | 14400000 | F2 | B | A | 2025-01-01 12:00:00 | 2025-01-01 13:00:00 | 60 |
5055
+
5056
+ The return journey takes four hours (14,400,000 milliseconds), including the
5057
+ two-hour layover at B.
5058
+
5059
+ Add up prices while continuing to report elapsed time for every cycle:
5060
+
5061
+ ```ts
5062
+ await flights
5063
+ .findCycles("origin", "destination", "flightId", "A", {
5064
+ startTimeColumn: "departure",
5065
+ endTimeColumn: "arrival",
5066
+ minGapMs: 60 * 60 * 1000,
5067
+ elapsedTime: true,
5068
+ weight: "price",
5069
+ })
5070
+ .log();
5071
+ ```
5072
+
5073
+ | start | pathId | step | weight | total | elapsedTimeMs | flightId | origin | destination | departure | arrival | price |
5074
+ | ----- | -----: | ---: | -----: | ----: | ------------: | -------- | ------ | ----------- | ------------------- | ------------------- | ----: |
5075
+ | A | 0 | 1 | 40 | 40 | 3600000 | F1 | A | B | 2025-01-01 09:00:00 | 2025-01-01 10:00:00 | 40 |
5076
+ | A | 0 | 2 | 60 | 100 | 14400000 | F2 | B | A | 2025-01-01 12:00:00 | 2025-01-01 13:00:00 | 60 |
5077
+
4758
5078
  #### `extractDatePart`
4759
5079
 
4760
5080
  Extracts one or more components from a temporal column into new columns. Pass a