@toclocoinc/lattice-grid 1.16.0 → 1.17.0
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/README.md +1 -1
- package/docs/API.html +238 -6
- package/docs/api-detail.html +109 -11
- package/lattice-grid.d.ts +214 -6
- package/lattice-grid.esm.min.js +1457 -542
- package/lattice-grid.min.cjs +1457 -542
- package/lattice-grid.min.css +1 -1
- package/lattice-grid.min.js +1457 -542
- package/modules/charts.esm.min.js +2 -2
- package/modules/devtools.esm.min.js +2 -2
- package/modules/dhtmlx-compat.esm.min.js +46 -4
- package/modules/htmx.esm.min.js +1457 -542
- package/modules/htmx.min.cjs +1457 -542
- package/modules/htmx.min.js +1457 -542
- package/modules/react.esm.min.js +2 -2
- package/modules/svelte.esm.min.js +2 -2
- package/modules/vue.esm.min.js +2 -2
- package/modules/webcomponent.esm.min.js +1457 -542
- package/package.json +1 -1
package/docs/api-detail.html
CHANGED
|
@@ -437,7 +437,7 @@
|
|
|
437
437
|
<div class="shell">
|
|
438
438
|
<aside class="rail">
|
|
439
439
|
<p class="rail__brand">Lattice Grid</p>
|
|
440
|
-
<p class="rail__sub">Developer guide · v1.
|
|
440
|
+
<p class="rail__sub">Developer guide · v1.17.0</p>
|
|
441
441
|
<nav>
|
|
442
442
|
<div class="rail__group">
|
|
443
443
|
<span class="rail__label">Start here</span>
|
|
@@ -2554,9 +2554,9 @@ grid.destroy();
|
|
|
2554
2554
|
</p>
|
|
2555
2555
|
|
|
2556
2556
|
<div class="example">
|
|
2557
|
-
<p class="example__label">
|
|
2558
|
-
<pre><code>createGrid(element, { stickyGroupHeaders: <span class="kw">
|
|
2559
|
-
createGrid(element, { stickyGroupHeaders: 3 }); <span class="cmt">//
|
|
2557
|
+
<p class="example__label">Off by default; opt in or set the cap</p>
|
|
2558
|
+
<pre><code>createGrid(element, { stickyGroupHeaders: <span class="kw">true</span> }); <span class="cmt">// on, up to two</span>
|
|
2559
|
+
createGrid(element, { stickyGroupHeaders: 3 }); <span class="cmt">// on, up to three</span></code></pre>
|
|
2560
2560
|
</div>
|
|
2561
2561
|
|
|
2562
2562
|
<div class="why">
|
|
@@ -2608,7 +2608,8 @@ createGrid(element, { stickyGroupHeaders: 3 }); <span class="cmt">// stack
|
|
|
2608
2608
|
<table>
|
|
2609
2609
|
<thead><tr><th>Option</th><th>Type</th><th>Description</th></tr></thead>
|
|
2610
2610
|
<tbody>
|
|
2611
|
-
<tr><td class="name">retainSource</td><td class="type">boolean</td><td class="desc">Default <code>true</code>. Set <code>false</code> to keep only the packed columns and reconstruct a plain row object from them on demand. <code>rows.data()</code> then returns freshly reconstructed objects — a new object each call — so <code>row === sourceObject</code> and a custom renderer reading <code>row.sourceObject</code> no longer hold, and equality becomes value-based. Cell values are identical either way, so <code>get()</code>, <code>byKey()</code>, <code>value()</code> and <code>values()</code> are unaffected.</td></tr>
|
|
2611
|
+
<tr><td class="name">retainSource</td><td class="type">boolean</td><td class="desc">Default <code>true</code>. Set <code>false</code> to keep only the packed columns and reconstruct a plain row object from them on demand. <code>rows.data()</code> then returns freshly reconstructed objects — a new object each call — so <code>row === sourceObject</code> and a custom renderer reading <code>row.sourceObject</code> no longer hold, and equality becomes value-based. Cell values are identical either way, so <code>get()</code>, <code>byKey()</code>, <code>value()</code> and <code>values()</code> are unaffected. On its own this drops only the store's reference array, not the objects: the source layer still holds them.</td></tr>
|
|
2612
|
+
<tr><td class="name">dropSourceRows</td><td class="type">boolean</td><td class="desc">Default <code>false</code>. Set <code>true</code> to release the caller's row objects from the <em>source layer</em> and the grid config once the store is built, so the packed columns are the only resident copy. This is where the large reduction lives — roughly an order of magnitude at a million rows — because the caller's objects are the dominant term. Implies <code>retainSource: false</code> (the store must reconstruct), so it carries the same identity tradeoff. An impure computed column — a shadow or a rank/positional column — is never stored, so it cannot be served from the columns: it warns once and drops out rather than answering wrongly, and <code>rows.move()</code> is refused. Do not enable it on a grid that sorts, filters, groups or totals on such a column, or that reorders rows in place.</td></tr>
|
|
2612
2613
|
</tbody>
|
|
2613
2614
|
</table>
|
|
2614
2615
|
</div>
|
|
@@ -2618,14 +2619,17 @@ createGrid(element, { stickyGroupHeaders: 3 }); <span class="cmt">// stack
|
|
|
2618
2619
|
columns,
|
|
2619
2620
|
rowKey: 'id',
|
|
2620
2621
|
rows,
|
|
2621
|
-
|
|
2622
|
+
<span class="cmt">// Release the caller's objects entirely; the packed columns are the sole copy.</span>
|
|
2623
|
+
ingest: { dropSourceRows: <span class="kw">true</span> },
|
|
2622
2624
|
});</code></pre>
|
|
2623
2625
|
</div>
|
|
2624
2626
|
<div class="why">
|
|
2625
|
-
<p>
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2627
|
+
<p>With <code>retainSource: false</code> alone the saving is only the store's own copy of the
|
|
2628
|
+
object references, not the objects themselves: the source layer and the grid config still
|
|
2629
|
+
hold the array, so whoever handed the grid its rows keeps them alive. <code>dropSourceRows</code>
|
|
2630
|
+
releases those references too, so once the caller lets go the objects can be collected and the
|
|
2631
|
+
grid becomes the sole holder of the data. Reach for it on a large, read-mostly grid where you
|
|
2632
|
+
can let go of the source array and do not depend on caller identity through
|
|
2629
2633
|
<code>rows.data()</code>.</p>
|
|
2630
2634
|
</div>
|
|
2631
2635
|
|
|
@@ -2746,7 +2750,8 @@ createGrid(el, { columns, rowKey: 'id', source });
|
|
|
2746
2750
|
plan.pushed; <span class="cmt">// the query the adapter was given</span>
|
|
2747
2751
|
plan.residual; <span class="cmt">// { filters, sort, quick } the grid applied after</span>
|
|
2748
2752
|
plan.unpushed; <span class="cmt">// ['filter'], the parts that stayed behind</span>
|
|
2749
|
-
plan.needsAll; <span class="cmt">// true when the whole result had to be fetched</span
|
|
2753
|
+
plan.needsAll; <span class="cmt">// true when the whole result had to be fetched</span>
|
|
2754
|
+
plan.full; <span class="cmt">// true when fullDataset forced it, not just residual work</span></code></pre>
|
|
2750
2755
|
</div>
|
|
2751
2756
|
<p>
|
|
2752
2757
|
The grid also warns once, naming the predicate that could not be pushed, because the fix is
|
|
@@ -2788,6 +2793,78 @@ createGrid(el, {
|
|
|
2788
2793
|
with no server involved.</p>
|
|
2789
2794
|
</div>
|
|
2790
2795
|
|
|
2796
|
+
<h3 id="fulldataset">Whole-dataset statistics over a remote source</h3>
|
|
2797
|
+
<p class="lead-in">
|
|
2798
|
+
A windowed source reduces a total or statistic over the rows it has loaded, not the whole
|
|
2799
|
+
matching set — a footer median of the 200 rows on screen, which is wrong and looks right.
|
|
2800
|
+
<code>fullDataset</code> makes the source hold the entire matching set client-side so those
|
|
2801
|
+
figures are computed over everything.
|
|
2802
|
+
</p>
|
|
2803
|
+
<div class="example">
|
|
2804
|
+
<p class="example__label">Correct footer figures over a REST or DuckDB source</p>
|
|
2805
|
+
<pre><code><span class="kw">const</span> source = createPushdownSource({
|
|
2806
|
+
adapter: restAdapter({ url: '/api/trades' }),
|
|
2807
|
+
fullDataset: {
|
|
2808
|
+
enabled: <span class="kw">true</span>, <span class="cmt">// hold the whole matching set, once per query</span>
|
|
2809
|
+
maxRows: 1_000_000, <span class="cmt">// refuse (visible error) past this</span>
|
|
2810
|
+
maxBytesEstimate: 512 * 1024 * 1024,
|
|
2811
|
+
},
|
|
2812
|
+
});</code></pre>
|
|
2813
|
+
</div>
|
|
2814
|
+
<div class="why">
|
|
2815
|
+
<p>It is <strong>off by default</strong> and reuses the same whole-result path that residual
|
|
2816
|
+
work already takes: the flag ORs into <code>needsAll</code>, so once the set is held it is
|
|
2817
|
+
ordinary in-memory data and the grid's existing total and statistics kernels reduce over all of
|
|
2818
|
+
it, with no per-stat change. When it is active and within the limits, the whole matching set is
|
|
2819
|
+
covered, so the windowed-statistic warning (BACKLOG-0000731) stays silent — the figure is now
|
|
2820
|
+
honestly whole-dataset.</p>
|
|
2821
|
+
<p><strong>It is refused loudly, never truncated.</strong> A matching set past
|
|
2822
|
+
<code>maxRows</code> or <code>maxBytesEstimate</code> is thrown and surfaced as a
|
|
2823
|
+
<code>source:error</code> with no rows shown, rather than held as a fraction and presented as
|
|
2824
|
+
the whole. A fraction shown as the whole is exactly the silent wrong answer this feature
|
|
2825
|
+
exists to remove, so it is never how the feature fails. For a <code>restAdapter</code>, which
|
|
2826
|
+
cannot compute, this is the only route to a correct whole-dataset statistic.</p>
|
|
2827
|
+
</div>
|
|
2828
|
+
|
|
2829
|
+
<h3 id="aggregates">Pushing statistics down to the engine</h3>
|
|
2830
|
+
<p class="lead-in">
|
|
2831
|
+
A DuckDB-class engine computes a median or a standard deviation over the whole matching set far
|
|
2832
|
+
faster than pulling every row here to do it. The <code>aggregates</code> config decides, at
|
|
2833
|
+
grid setup, which statistics the engine computes and which the grid does — a design-time
|
|
2834
|
+
developer choice, fixed for the life of the grid, never a runtime toggle and never shown to an
|
|
2835
|
+
end user.
|
|
2836
|
+
</p>
|
|
2837
|
+
<div class="example">
|
|
2838
|
+
<p class="example__label">Push the verified-identical stats, keep the rest exact</p>
|
|
2839
|
+
<pre><code><span class="kw">const</span> source = createPushdownSource({
|
|
2840
|
+
adapter: duckdbAdapter({ connection: conn, from: <span class="str">'trades'</span> }),
|
|
2841
|
+
aggregates: {
|
|
2842
|
+
<span class="cmt">// 'engine' pushes everything expressible; 'engine-if-identical' pushes only</span>
|
|
2843
|
+
<span class="cmt">// the stats whose engine result is verified identical to the grid kernel;</span>
|
|
2844
|
+
<span class="cmt">// 'client' (the default when absent) computes everything here.</span>
|
|
2845
|
+
<span class="kw">default</span>: <span class="str">'engine-if-identical'</span>,
|
|
2846
|
+
overrides: { mode: <span class="str">'client'</span> }, <span class="cmt">// I want the grid's null-when-distinct mode</span>
|
|
2847
|
+
},
|
|
2848
|
+
});</code></pre>
|
|
2849
|
+
</div>
|
|
2850
|
+
<div class="why">
|
|
2851
|
+
<p>Every statistic is classified <strong>IDENTICAL</strong> (the engine's result equals the
|
|
2852
|
+
grid's own kernel, verified against it on the same data) or <strong>MAY-DIFFER</strong> (the
|
|
2853
|
+
engine computes it by a method that can differ from the grid's definition — <code>mode</code>
|
|
2854
|
+
returns a value where the grid returns null). The classification drives the docs and
|
|
2855
|
+
build-time provenance, <em>not</em> whether a stat is pushed: that is your choice.
|
|
2856
|
+
<code>weightedQuantile</code> is the one genuine fallback, always client-side, because the
|
|
2857
|
+
engine cannot express the grid's midpoint convention. The published table of every stat, its
|
|
2858
|
+
class and its SQL is generated from one map (<code>STAT_PUSHDOWN</code>) so it cannot drift.</p>
|
|
2859
|
+
<p><strong>No mixed provenance.</strong> An engine figure and a client figure never appear in
|
|
2860
|
+
one result set. Aggregates are pushed only when the filter is <em>fully</em> pushed; a residual
|
|
2861
|
+
filter the engine could not apply forces every aggregate client-side, because an engine number
|
|
2862
|
+
computed over a superset beside a client number over the real set would be wrong-but-plausible.
|
|
2863
|
+
<code>source.lastPlan().aggregates</code> reports, per stat, whether the engine or the grid
|
|
2864
|
+
computed it and the class it was assigned — inspection during the build, not a per-figure
|
|
2865
|
+
runtime marker.</p>
|
|
2866
|
+
</div>
|
|
2867
|
+
|
|
2791
2868
|
<h2 id="derived">Grids built from other grids</h2>
|
|
2792
2869
|
<p class="lead-in">
|
|
2793
2870
|
A derived grid takes its rows from another grid rather than from a load: grouped and
|
|
@@ -2982,6 +3059,27 @@ columns: [
|
|
|
2982
3059
|
scrolling past a rounded corner is cut by it rather than squaring it off.
|
|
2983
3060
|
</p>
|
|
2984
3061
|
|
|
3062
|
+
<div class="example">
|
|
3063
|
+
<p class="example__label">Zebra striping (opt-in)</p>
|
|
3064
|
+
<pre><code>createGrid(element, {
|
|
3065
|
+
columns, rows,
|
|
3066
|
+
stripedRows: <span class="kw">true</span>, <span class="cmt">// shade alternate data rows; off by default</span>
|
|
3067
|
+
});</code></pre>
|
|
3068
|
+
</div>
|
|
3069
|
+
|
|
3070
|
+
<p class="lead-in">
|
|
3071
|
+
<code>stripedRows</code> shades every other data row. It is strictly opt-in and off by default,
|
|
3072
|
+
so a grid that never mentions it looks exactly as it did on upgrade. Parity is decided by each
|
|
3073
|
+
row's <em>logical</em> index rather than its position in the DOM: rows are virtualised and
|
|
3074
|
+
recycled, so a <code>:nth-child</code> rule would repaint the stripe onto whichever row landed
|
|
3075
|
+
in an odd slot after a scroll, and a logical-index stripe keeps a row shaded consistently across
|
|
3076
|
+
a scroll and across the left-pinned, centre and right-pinned segments of the same row. Group
|
|
3077
|
+
headings, group footers and the grand total are structure rather than data, so they are never
|
|
3078
|
+
striped. The stripe uses the theme's <code>--lattice-surface-alt</code> token, which every
|
|
3079
|
+
palette defines, so dark, high-contrast and terminal are correct without any extra rule, and
|
|
3080
|
+
both selection and hover still win over it.
|
|
3081
|
+
</p>
|
|
3082
|
+
|
|
2985
3083
|
<h2 id="cards">Cards, lists and feeds</h2>
|
|
2986
3084
|
<p class="lead-in">
|
|
2987
3085
|
<code>rowTemplate</code> draws each row with a layout of your own instead of dividing it into
|
package/lattice-grid.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Lattice Grid 1.
|
|
2
|
+
* Lattice Grid 1.17.0, type declarations
|
|
3
3
|
* Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
|
|
4
4
|
* https://latticegrid.dev
|
|
5
5
|
*/
|
|
@@ -779,6 +779,41 @@ export interface IngestConfig {
|
|
|
779
779
|
*/
|
|
780
780
|
retainSource?: boolean;
|
|
781
781
|
|
|
782
|
+
/**
|
|
783
|
+
* Release the caller's row objects from the *source layer* once the column
|
|
784
|
+
* store has been built, so the columns become the sole resident copy of the
|
|
785
|
+
* data. Default `false`, which keeps today's behaviour.
|
|
786
|
+
*
|
|
787
|
+
* `retainSource:false` stops the {@link https://en.wikipedia.org/wiki/Column-oriented_DBMS column store}
|
|
788
|
+
* from holding the caller's objects, but the memory source and the grid config
|
|
789
|
+
* still retain the supplied array by reference — so the objects stay alive and
|
|
790
|
+
* the resident footprint does not actually fall. This flag closes that gap: it
|
|
791
|
+
* clears `MemorySource`'s retained array and drops the array from the grid
|
|
792
|
+
* config, leaving nothing on the heap but the packed columns. That is where
|
|
793
|
+
* the large reduction comes from (roughly an order of magnitude at a million
|
|
794
|
+
* rows), not from `retainSource` on its own.
|
|
795
|
+
*
|
|
796
|
+
* Implies `retainSource:false`: dropping the caller's objects while the store
|
|
797
|
+
* still expects to read through them would leave the source with no data at
|
|
798
|
+
* all, so setting this on forces the store to reconstruct rows from columns.
|
|
799
|
+
* Every read is therefore served from the columns — `at()`, `byKey()`,
|
|
800
|
+
* `get()`, `value()`, `values()`, filtering, sorting, grouping, totals and
|
|
801
|
+
* export are all unaffected in their values. What changes is the same three
|
|
802
|
+
* identity behaviours `retainSource:false` documents: `rows.data()` returns
|
|
803
|
+
* freshly reconstructed objects (so `row === sourceObject` no longer holds), a
|
|
804
|
+
* custom renderer reaching for `row.sourceObject` gets a reconstruction, and
|
|
805
|
+
* equality against a row becomes value-based.
|
|
806
|
+
*
|
|
807
|
+
* One consumer cannot be served from the columns: an *impure computed column*
|
|
808
|
+
* (a shadow, or a rank/positional column) is deliberately never materialised
|
|
809
|
+
* into the store, so its handle is built by reading the source objects. Under
|
|
810
|
+
* `dropSourceRows` those objects are gone, so such a column reduces over
|
|
811
|
+
* nothing and warns once rather than returning a silently wrong figure. Do not
|
|
812
|
+
* enable `dropSourceRows` on a grid that sorts, filters, groups or totals on a
|
|
813
|
+
* shadow or a positional column.
|
|
814
|
+
*/
|
|
815
|
+
dropSourceRows?: boolean;
|
|
816
|
+
|
|
782
817
|
/**
|
|
783
818
|
* Columnize `stream`-source ingest on a Worker so a large load does not block
|
|
784
819
|
* the main thread. Default `false`. When on, an arriving chunk that clears
|
|
@@ -1158,6 +1193,21 @@ export interface GridConfig {
|
|
|
1158
1193
|
*/
|
|
1159
1194
|
cornerRadius?: boolean | number | string;
|
|
1160
1195
|
|
|
1196
|
+
/**
|
|
1197
|
+
* Shade alternate data rows (zebra striping).
|
|
1198
|
+
*
|
|
1199
|
+
* Off by default, and strictly opt-in: an existing grid must look exactly the
|
|
1200
|
+
* same on upgrade. When `true`, every other data row takes the theme's
|
|
1201
|
+
* `--lattice-surface-alt` background, which every palette already defines, so
|
|
1202
|
+
* dark, high-contrast and terminal stripe correctly without extra work.
|
|
1203
|
+
*
|
|
1204
|
+
* Parity follows the row's *logical* index, not its position in the DOM, so a
|
|
1205
|
+
* row keeps its stripe across a scroll even though the rows are recycled.
|
|
1206
|
+
* Structural rows — group headings, group footers and the grand total — are
|
|
1207
|
+
* never striped, and both selection and hover still win over the stripe.
|
|
1208
|
+
*/
|
|
1209
|
+
stripedRows?: boolean;
|
|
1210
|
+
|
|
1161
1211
|
/**
|
|
1162
1212
|
* Show a bar above the column headings for filtering columns by tag.
|
|
1163
1213
|
*
|
|
@@ -1312,7 +1362,12 @@ export interface GridConfig {
|
|
|
1312
1362
|
* only the sort, filter and menu controls inside them.
|
|
1313
1363
|
*/
|
|
1314
1364
|
showHeader?: boolean;
|
|
1315
|
-
/**
|
|
1365
|
+
/**
|
|
1366
|
+
* Header height in pixels. Omitted, the header takes its height from the
|
|
1367
|
+
* density-scaled `--lattice-header-height` token, so `density` sizes the
|
|
1368
|
+
* header as it sizes the rows. A number names one explicitly and outranks the
|
|
1369
|
+
* token.
|
|
1370
|
+
*/
|
|
1316
1371
|
headerHeight?: number;
|
|
1317
1372
|
/** How many rows to render beyond the viewport. More costs memory and
|
|
1318
1373
|
* smooths fast scrolling; fewer is lighter and can show a gap. */
|
|
@@ -1562,9 +1617,11 @@ export interface GridConfig {
|
|
|
1562
1617
|
* Keep the enclosing group headings pinned above the viewport while
|
|
1563
1618
|
* scrolling inside a group.
|
|
1564
1619
|
*
|
|
1565
|
-
*
|
|
1566
|
-
* `
|
|
1567
|
-
*
|
|
1620
|
+
* Off by default — a deliberate product default; sticky group headers are
|
|
1621
|
+
* opt-in. `true` turns it on, stacking at most two; a number, or `{ depth }`,
|
|
1622
|
+
* sets how many may stack: each costs a row of viewport, so a deep grouping
|
|
1623
|
+
* would otherwise spend the screen describing itself. `false` is off, the
|
|
1624
|
+
* same as leaving it unset.
|
|
1568
1625
|
*/
|
|
1569
1626
|
stickyGroupHeaders?: boolean | number | { depth?: number };
|
|
1570
1627
|
/**
|
|
@@ -1892,6 +1949,103 @@ export interface PushdownPlan {
|
|
|
1892
1949
|
needsAll: boolean;
|
|
1893
1950
|
/** Which parts could not be pushed: `filter`, `sort`, `quick`. */
|
|
1894
1951
|
unpushed: string[];
|
|
1952
|
+
/**
|
|
1953
|
+
* Whether the whole result was fetched because `fullDataset` is on, rather
|
|
1954
|
+
* than only because residual work forced it. When true, totals and statistics
|
|
1955
|
+
* reduce over the whole matching set and the windowed-stat warning is silent.
|
|
1956
|
+
*/
|
|
1957
|
+
full: boolean;
|
|
1958
|
+
/**
|
|
1959
|
+
* Per-aggregate provenance, present only when the last request computed
|
|
1960
|
+
* aggregates (BACKLOG-0000730 Part B): which statistics the engine computed
|
|
1961
|
+
* and which the client did, with the class the pushdown map assigned each.
|
|
1962
|
+
* Under grouping it also carries the `groupBy` the subtotals were computed
|
|
1963
|
+
* over. Build-time inspection, not a runtime per-figure marker.
|
|
1964
|
+
*/
|
|
1965
|
+
aggregates?: {
|
|
1966
|
+
engine: AggregateProvenance[];
|
|
1967
|
+
client: AggregateProvenance[];
|
|
1968
|
+
groupBy?: string[];
|
|
1969
|
+
};
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
/**
|
|
1973
|
+
* Opt-in, sticky full-dataset pull for a pushdown/remote source
|
|
1974
|
+
* (BACKLOG-0000730). Off by default. When enabled, the source materialises the
|
|
1975
|
+
* entire matching set client-side once per query signature and serves every
|
|
1976
|
+
* window, total and statistic from it, so those figures are computed over the
|
|
1977
|
+
* whole set rather than the loaded window. A set past either limit is refused
|
|
1978
|
+
* with a visible `source:error` — never silently truncated.
|
|
1979
|
+
*/
|
|
1980
|
+
export interface PushdownFullDatasetConfig {
|
|
1981
|
+
/** Sticky: hold the whole matching set client-side. Default `false`. */
|
|
1982
|
+
enabled?: boolean;
|
|
1983
|
+
/** Refuse (visible error) past this many rows. Default `1_000_000`. */
|
|
1984
|
+
maxRows?: number;
|
|
1985
|
+
/** Refuse past this estimated heap cost, in bytes. Default `512 * 1024 * 1024`. */
|
|
1986
|
+
maxBytesEstimate?: number;
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
/** How one requested aggregate should be computed. */
|
|
1990
|
+
export type AggregateMode = 'engine' | 'client' | 'engine-if-identical';
|
|
1991
|
+
|
|
1992
|
+
/**
|
|
1993
|
+
* Design-time aggregate-pushdown policy for a pushdown source
|
|
1994
|
+
* (BACKLOG-0000730 Part B, ungrouped). The developer chooses, at grid setup
|
|
1995
|
+
* before render, whether each statistic is computed by the engine (fast, over
|
|
1996
|
+
* the matching set) or client-side (the grid's exact definition, needs a
|
|
1997
|
+
* full-dataset pull). It is fixed for the life of the grid, never a runtime
|
|
1998
|
+
* toggle, and never surfaced to an end user.
|
|
1999
|
+
*
|
|
2000
|
+
* Absent, every aggregate is computed client-side — today's behaviour, so no
|
|
2001
|
+
* existing caller regresses. `engine-if-identical` is the recommended setting
|
|
2002
|
+
* for a windowed DuckDB source: it pushes only the statistics whose engine
|
|
2003
|
+
* result is verified identical to the grid kernel, keeping the documented
|
|
2004
|
+
* MAY-DIFFER stats (e.g. `mode`) client-side. The engine is used only when the
|
|
2005
|
+
* filter is fully pushed; a residual filter forces every aggregate client-side,
|
|
2006
|
+
* so an engine figure and a client figure never mix in one result set.
|
|
2007
|
+
*/
|
|
2008
|
+
export interface PushdownAggregatesConfig {
|
|
2009
|
+
/**
|
|
2010
|
+
* The default policy for stats the engine can express. `'engine'` pushes
|
|
2011
|
+
* everything expressible (using the engine's method for MAY-DIFFER stats);
|
|
2012
|
+
* `'engine-if-identical'` pushes only the verified-identical ones; `'client'`
|
|
2013
|
+
* computes everything client-side. Default `'client'`.
|
|
2014
|
+
*/
|
|
2015
|
+
default?: AggregateMode;
|
|
2016
|
+
/** Per-stat overrides, winning over `default`. A stat the engine cannot
|
|
2017
|
+
* express (`weightedQuantile`) is always client-side regardless. */
|
|
2018
|
+
overrides?: Record<string, 'engine' | 'client'>;
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
/**
|
|
2022
|
+
* One aggregate the grid asks the source to compute over the matching set.
|
|
2023
|
+
* `params` carries e.g. `{ share: 0.1 }` so an adapter emits the matching SQL;
|
|
2024
|
+
* `weight` names the second column for a two-column stat like `correlation`.
|
|
2025
|
+
*/
|
|
2026
|
+
export interface AggregateRequest {
|
|
2027
|
+
/** Keys the result back to the request. */
|
|
2028
|
+
id: string;
|
|
2029
|
+
/** The column to reduce. */
|
|
2030
|
+
col: string;
|
|
2031
|
+
/** The statistic name, as used in `total: '<name>'`. */
|
|
2032
|
+
fn: string;
|
|
2033
|
+
/** The second column, for a two-column statistic. */
|
|
2034
|
+
weight?: string;
|
|
2035
|
+
/** Parameters the statistic takes, e.g. a trim share. */
|
|
2036
|
+
params?: Record<string, unknown>;
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
/** How one aggregate was routed, for `lastPlan()` provenance. */
|
|
2040
|
+
export interface AggregateProvenance {
|
|
2041
|
+
id: string;
|
|
2042
|
+
col: string;
|
|
2043
|
+
fn: string;
|
|
2044
|
+
/** How the engine result relates to the grid kernel. */
|
|
2045
|
+
class: 'identical' | 'may-differ' | 'fallback';
|
|
2046
|
+
/** Why it is client-side, when it is (config, fallback, or the guard). */
|
|
2047
|
+
reason?: string;
|
|
2048
|
+
weight?: string;
|
|
1895
2049
|
}
|
|
1896
2050
|
|
|
1897
2051
|
export interface PushdownSourceConfig {
|
|
@@ -1899,6 +2053,16 @@ export interface PushdownSourceConfig {
|
|
|
1899
2053
|
/** The compute barrel, for applying whatever the engine could not. */
|
|
1900
2054
|
compute?: object;
|
|
1901
2055
|
pageSize?: number;
|
|
2056
|
+
/**
|
|
2057
|
+
* Opt-in full-dataset pull. Off unless `fullDataset.enabled` is set. See
|
|
2058
|
+
* {@link PushdownFullDatasetConfig}.
|
|
2059
|
+
*/
|
|
2060
|
+
fullDataset?: PushdownFullDatasetConfig;
|
|
2061
|
+
/**
|
|
2062
|
+
* Design-time aggregate-pushdown policy. Absent = client-side (today's
|
|
2063
|
+
* behaviour). See {@link PushdownAggregatesConfig}.
|
|
2064
|
+
*/
|
|
2065
|
+
aggregates?: PushdownAggregatesConfig;
|
|
1902
2066
|
}
|
|
1903
2067
|
|
|
1904
2068
|
export interface StatisticsApi {
|
|
@@ -3414,7 +3578,51 @@ export function toneOf(direction: string, goodWhen: string): 'good' | 'bad' | 'f
|
|
|
3414
3578
|
*/
|
|
3415
3579
|
export function createPushdownSource(
|
|
3416
3580
|
config: PushdownSourceConfig,
|
|
3417
|
-
): SourceConfig & {
|
|
3581
|
+
): SourceConfig & {
|
|
3582
|
+
lastPlan(): PushdownPlan | null;
|
|
3583
|
+
/**
|
|
3584
|
+
* Compute a set of aggregates over the matching set, splitting them between
|
|
3585
|
+
* the engine and the client by the design-time `aggregates` config
|
|
3586
|
+
* (BACKLOG-0000730 Part B). Ungrouped, returns the engine-computed `values`
|
|
3587
|
+
* keyed by id. When the request carries a `groupBy`, returns `groups` instead:
|
|
3588
|
+
* one entry per subtotal level and the grand total (`level: 0`, produced by a
|
|
3589
|
+
* single `GROUP BY ROLLUP`), each with its key values and its aggregate values
|
|
3590
|
+
* keyed by id. The client list is what the caller computes itself over the
|
|
3591
|
+
* full set. Aggregates are pushed only when the filter is fully pushed and —
|
|
3592
|
+
* under grouping — every grouping key is a plain column the engine can group
|
|
3593
|
+
* by; a residual filter or an unpushable group key forces every aggregate
|
|
3594
|
+
* client-side (no mixed provenance).
|
|
3595
|
+
*/
|
|
3596
|
+
aggregate(
|
|
3597
|
+
request: RemoteRequest,
|
|
3598
|
+
requested: AggregateRequest[],
|
|
3599
|
+
): Promise<{
|
|
3600
|
+
values: Record<string, unknown>;
|
|
3601
|
+
groups?: Array<{
|
|
3602
|
+
keys: unknown[];
|
|
3603
|
+
grouping?: number[];
|
|
3604
|
+
level: number;
|
|
3605
|
+
values: Record<string, unknown>;
|
|
3606
|
+
}>;
|
|
3607
|
+
engine: AggregateProvenance[];
|
|
3608
|
+
client: AggregateProvenance[];
|
|
3609
|
+
}>;
|
|
3610
|
+
};
|
|
3611
|
+
|
|
3612
|
+
/**
|
|
3613
|
+
* The pushdown map (BACKLOG-0000730 Part B): one published record per statistic
|
|
3614
|
+
* giving whether the engine can express it, the DuckDB aggregate SQL it emits,
|
|
3615
|
+
* and whether that result is IDENTICAL to the grid's own kernel or MAY-DIFFER.
|
|
3616
|
+
* The single source of truth the push router, the docs and `lastPlan()` all read.
|
|
3617
|
+
*/
|
|
3618
|
+
export const STAT_PUSHDOWN: Readonly<Record<string, {
|
|
3619
|
+
pushable: boolean;
|
|
3620
|
+
class: 'identical' | 'may-differ' | 'fallback';
|
|
3621
|
+
sql?: string;
|
|
3622
|
+
note?: string;
|
|
3623
|
+
twoColumn?: boolean;
|
|
3624
|
+
blankAware?: boolean;
|
|
3625
|
+
}>>;
|
|
3418
3626
|
|
|
3419
3627
|
/**
|
|
3420
3628
|
* The capability set an adapter that declares nothing is treated as having:
|