data-finder-ts 0.1.3 → 0.1.5
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 +17 -55
- package/dist/chunk-BGEX377Q.js +230 -0
- package/dist/chunk-BGEX377Q.js.map +1 -0
- package/dist/chunk-BKPWV3OF.js +962 -0
- package/dist/chunk-BKPWV3OF.js.map +1 -0
- package/dist/{chunk-EYXBYV2X.js → chunk-GPHWC533.js} +2 -2
- package/dist/chunk-KAK75RVS.js +198 -0
- package/dist/chunk-KAK75RVS.js.map +1 -0
- package/dist/chunk-MGEL5RWM.js +628 -0
- package/dist/chunk-MGEL5RWM.js.map +1 -0
- package/dist/chunk-PF6EJZHB.js +1015 -0
- package/dist/chunk-PF6EJZHB.js.map +1 -0
- package/dist/datafinder/attribute.cjs +595 -18
- package/dist/datafinder/attribute.cjs.map +1 -1
- package/dist/datafinder/attribute.d.cts +73 -10
- package/dist/datafinder/attribute.d.ts +73 -10
- package/dist/datafinder/attribute.js +2 -2
- package/dist/datafinder/runner.cjs +1589 -288
- package/dist/datafinder/runner.cjs.map +1 -1
- package/dist/datafinder/runner.d.cts +41 -7
- package/dist/datafinder/runner.d.ts +41 -7
- package/dist/datafinder/runner.js +12 -6
- package/dist/datafinder/sql-generator.cjs +1484 -281
- package/dist/datafinder/sql-generator.cjs.map +1 -1
- package/dist/datafinder/sql-generator.d.cts +19 -14
- package/dist/datafinder/sql-generator.d.ts +19 -14
- package/dist/datafinder/sql-generator.js +9 -7
- package/dist/datafinder/typed-attributes.cjs +856 -93
- package/dist/datafinder/typed-attributes.cjs.map +1 -1
- package/dist/datafinder/typed-attributes.d.cts +3 -135
- package/dist/datafinder/typed-attributes.d.ts +3 -135
- package/dist/datafinder/typed-attributes.js +3 -3
- package/dist/dialect-CogGPm_y.d.cts +38 -0
- package/dist/dialect-aaG1bDi5.d.ts +38 -0
- package/dist/index.cjs +2080 -480
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +119 -14
- package/dist/index.d.ts +119 -14
- package/dist/index.js +362 -12
- package/dist/index.js.map +1 -1
- package/dist/model/milestoning.cjs +529 -0
- package/dist/model/milestoning.cjs.map +1 -1
- package/dist/model/milestoning.js +2 -2
- package/dist/model/relational.cjs +608 -12
- package/dist/model/relational.cjs.map +1 -1
- package/dist/model/relational.d.cts +456 -47
- package/dist/model/relational.d.ts +456 -47
- package/dist/model/relational.js +61 -3
- package/dist/typed-attributes-CUTf_6Ru.d.ts +328 -0
- package/dist/typed-attributes-CdWXq_78.d.cts +328 -0
- package/package.json +5 -2
- package/dist/chunk-3SSWFES4.js +0 -448
- package/dist/chunk-3SSWFES4.js.map +0 -1
- package/dist/chunk-5K2Z3YDY.js +0 -393
- package/dist/chunk-5K2Z3YDY.js.map +0 -1
- package/dist/chunk-6QTCGJWX.js +0 -98
- package/dist/chunk-6QTCGJWX.js.map +0 -1
- package/dist/chunk-DFYZVBII.js +0 -652
- package/dist/chunk-DFYZVBII.js.map +0 -1
- package/dist/chunk-V647VNMY.js +0 -135
- package/dist/chunk-V647VNMY.js.map +0 -1
- /package/dist/{chunk-EYXBYV2X.js.map → chunk-GPHWC533.js.map} +0 -0
package/README.md
CHANGED
|
@@ -1,69 +1,42 @@
|
|
|
1
1
|
# data-finder-ts
|
|
2
2
|
|
|
3
|
-
TypeScript port of [data-finder](https://github.com/jackie-h/data-finder), a model-driven SQL query builder with temporal (milestoned) data support. A Python generator reads
|
|
3
|
+
TypeScript port of [data-finder](https://github.com/jackie-h/data-finder), a model-driven SQL query builder with temporal (milestoned) data support. A Python generator reads mapping definitions and emits strongly-typed TypeScript finder classes.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
For runtime usage and query API see **[FINDERS.md](FINDERS.md)**.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Requirements
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
- Node ≥ 24
|
|
10
|
+
- [uv](https://docs.astral.sh/uv/) (Python package manager)
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
const rows = await tf.findAll(
|
|
14
|
-
null, // business date (null = not applicable)
|
|
15
|
-
new Date(), // processing valid-at (temporal snapshot)
|
|
16
|
-
[tf.symbol(), tf.price(), tf.account().name()],
|
|
17
|
-
tf.symbol().eq('AAPL'),
|
|
18
|
-
).toRows();
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Temporal filtering (`in_z <= ? AND out_z > ?`) is injected automatically based on the milestoning type declared in the mapping.
|
|
22
|
-
|
|
23
|
-
## Setup
|
|
12
|
+
## Install
|
|
24
13
|
|
|
25
14
|
```bash
|
|
26
15
|
npm install
|
|
27
|
-
|
|
28
|
-
# Install Python generator dependencies (system-wide, no virtualenv)
|
|
29
|
-
pip3 install jinja2 markdown-it-py
|
|
16
|
+
uv sync # install Python generator dependencies
|
|
30
17
|
```
|
|
31
18
|
|
|
32
|
-
Node ≥ 20 required.
|
|
33
|
-
|
|
34
19
|
## Generating finders
|
|
35
20
|
|
|
36
|
-
Generated files are gitignored — regenerate
|
|
21
|
+
Generated files are gitignored — regenerate before running tests on a fresh clone.
|
|
37
22
|
|
|
38
23
|
```bash
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
# From a markdown mapping file (finance_mapping.md in sibling data-finder repo)
|
|
43
|
-
python3 example/generate_from_markdown.py # → tests/generated_markdown/
|
|
24
|
+
npm run generate # from programmatic mappings (example/mappings.py) → example/generated/
|
|
25
|
+
npm run generate:examples # from all datafinder_examples mappings → tests/generated_examples/
|
|
44
26
|
```
|
|
45
27
|
|
|
46
|
-
The
|
|
28
|
+
The generator uses the [`data-finder`](https://pypi.org/project/data-finder/) PyPI package (installed via `uv sync`).
|
|
47
29
|
|
|
48
30
|
## Running tests
|
|
49
31
|
|
|
50
32
|
```bash
|
|
51
|
-
npm test
|
|
52
|
-
npx vitest run tests/duckdb.test.ts
|
|
53
|
-
npm run build
|
|
33
|
+
npm test # all tests
|
|
34
|
+
npx vitest run tests/duckdb.test.ts # single file
|
|
35
|
+
npm run build # type-check only
|
|
36
|
+
npm run build:lib # build distributable (tsup)
|
|
54
37
|
```
|
|
55
38
|
|
|
56
|
-
Tests use an in-memory DuckDB instance via `@duckdb/node-api`.
|
|
57
|
-
|
|
58
|
-
## Milestoning types
|
|
59
|
-
|
|
60
|
-
| Type | Mapping class | `findAll` args used |
|
|
61
|
-
|---|---|---|
|
|
62
|
-
| None | — | neither |
|
|
63
|
-
| Processing temporal | `ProcessingDateMilestonesPropertyMapping` | `processingValidAt` |
|
|
64
|
-
| Single business date | `SingleBusinessDateMilestonePropertyMapping` | `businessDate` |
|
|
65
|
-
| Business date + processing | `BusinessDateAndProcessingMilestonePropertyMapping` | both |
|
|
66
|
-
| Bi-temporal | `BiTemporalMilestonePropertyMapping` | both |
|
|
39
|
+
Tests use an in-memory DuckDB instance via `@duckdb/node-api`.
|
|
67
40
|
|
|
68
41
|
## Defining a mapping (Markdown)
|
|
69
42
|
|
|
@@ -98,20 +71,9 @@ Tests use an in-memory DuckDB instance via `@duckdb/node-api`. `tests/duckdb.tes
|
|
|
98
71
|
Load and generate:
|
|
99
72
|
|
|
100
73
|
```python
|
|
101
|
-
from
|
|
74
|
+
from datafinder_examples.finance_mapping import load
|
|
102
75
|
from ts_generator.generator import generate
|
|
103
76
|
|
|
104
77
|
mapping = load('my_mapping.md')
|
|
105
78
|
generate(mapping, 'output/')
|
|
106
79
|
```
|
|
107
|
-
|
|
108
|
-
## Reverse associations
|
|
109
|
-
|
|
110
|
-
When a model association is declared (e.g. `Trade → Account`), the generator adds a `trades()` method on `AccountFinder` pointing back to `TradeRelatedFinder`. Because ESM circular imports can't be resolved with static `import`, finders self-register at module load via `registerRelatedFinderClass`. When using a reverse association in tests, import the source finder's module first:
|
|
111
|
-
|
|
112
|
-
```typescript
|
|
113
|
-
await import('./generated/TradeFinder'); // ensures TradeRelatedFinder is registered
|
|
114
|
-
const { AccountFinder } = await import('./generated/AccountFinder');
|
|
115
|
-
const af = new AccountFinder();
|
|
116
|
-
const rows = await af.findAll(null, null, [af.name(), af.trades().symbol()]).toRows();
|
|
117
|
-
```
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AggregateOperation,
|
|
3
|
+
Alias,
|
|
4
|
+
Column,
|
|
5
|
+
ColumnWithJoin,
|
|
6
|
+
InOperation,
|
|
7
|
+
IsNotNullOperation,
|
|
8
|
+
IsNullOperation,
|
|
9
|
+
NotInOperation,
|
|
10
|
+
ScalarFunctionOperation,
|
|
11
|
+
SortOperation,
|
|
12
|
+
WindowFunctionOperation,
|
|
13
|
+
WindowSpecification
|
|
14
|
+
} from "./chunk-PF6EJZHB.js";
|
|
15
|
+
|
|
16
|
+
// src/datafinder/attribute.ts
|
|
17
|
+
var Attribute = class _Attribute {
|
|
18
|
+
_displayName;
|
|
19
|
+
_column;
|
|
20
|
+
_owner;
|
|
21
|
+
_parent;
|
|
22
|
+
_embedded;
|
|
23
|
+
_optional;
|
|
24
|
+
constructor(displayName, columnName, columnDbType, owner, parent, embedded, nullable = true, optional = false) {
|
|
25
|
+
this._optional = optional;
|
|
26
|
+
this._displayName = displayName;
|
|
27
|
+
this._column = new Column(columnName, columnDbType, owner, false, nullable);
|
|
28
|
+
this._owner = owner;
|
|
29
|
+
this._parent = parent;
|
|
30
|
+
this._embedded = embedded;
|
|
31
|
+
}
|
|
32
|
+
column() {
|
|
33
|
+
return this._column;
|
|
34
|
+
}
|
|
35
|
+
owner() {
|
|
36
|
+
return this._owner;
|
|
37
|
+
}
|
|
38
|
+
parent() {
|
|
39
|
+
return this._parent;
|
|
40
|
+
}
|
|
41
|
+
displayName() {
|
|
42
|
+
return this._displayName;
|
|
43
|
+
}
|
|
44
|
+
/** Alternate attribute on a flat column of the owning table, used in place of this one when its join is elided. */
|
|
45
|
+
embedded() {
|
|
46
|
+
return this._embedded;
|
|
47
|
+
}
|
|
48
|
+
/** @internal The expression a function of this attribute applies to: its column. */
|
|
49
|
+
functionInput() {
|
|
50
|
+
return this.cwj();
|
|
51
|
+
}
|
|
52
|
+
/** @internal What a function of this attribute is labelled after: its display name. */
|
|
53
|
+
functionLabel() {
|
|
54
|
+
return this._displayName;
|
|
55
|
+
}
|
|
56
|
+
/** @internal This attribute as an operand of arithmetic: its column. */
|
|
57
|
+
arithmeticOperand() {
|
|
58
|
+
return this.cwj();
|
|
59
|
+
}
|
|
60
|
+
cwj() {
|
|
61
|
+
return new ColumnWithJoin(this._column, this._parent, this._embedded?.cwj(), this._optional);
|
|
62
|
+
}
|
|
63
|
+
/** Count the number of non-null values. Use in a projection with `groupBy` to aggregate per group. */
|
|
64
|
+
count() {
|
|
65
|
+
return new AggregateOperation(
|
|
66
|
+
this.cwj(),
|
|
67
|
+
"COUNT" /* COUNT */,
|
|
68
|
+
this._displayName + " Count"
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
/** Name this column in the result: `f.price().sum().alias('Total')`. */
|
|
72
|
+
alias(name) {
|
|
73
|
+
return new Alias(this, name);
|
|
74
|
+
}
|
|
75
|
+
/** The number of different non-NULL values in the group: `COUNT(DISTINCT column)`. Use with `groupBy` to count per group. */
|
|
76
|
+
countDistinct() {
|
|
77
|
+
return new AggregateOperation(this.cwj(), "COUNT_DISTINCT" /* COUNT_DISTINCT */, this._displayName + " Count Distinct");
|
|
78
|
+
}
|
|
79
|
+
/** The value, or `defaultValue` when it is NULL: `name().orElse('N/A')`. Like `COALESCE` in SQL. */
|
|
80
|
+
orElse(defaultValue) {
|
|
81
|
+
if (!["string", "number", "boolean"].includes(typeof defaultValue)) {
|
|
82
|
+
throw new TypeError(`An orElse default is a string, number or boolean, not ${defaultValue === null ? "null" : typeof defaultValue}`);
|
|
83
|
+
}
|
|
84
|
+
return new ScalarFunctionOperation(this.cwj(), "COALESCE" /* COALESCE */, "Or Else " + this._displayName, void 0, [defaultValue]);
|
|
85
|
+
}
|
|
86
|
+
/** Sort this attribute in ascending order. Pass to `orderBy` or a window function's `orderBy`. */
|
|
87
|
+
ascending() {
|
|
88
|
+
return new SortOperation(this.cwj(), "ASC" /* ASC */);
|
|
89
|
+
}
|
|
90
|
+
/** Sort this attribute in descending order. Pass to `orderBy` or a window function's `orderBy`. */
|
|
91
|
+
descending() {
|
|
92
|
+
return new SortOperation(this.cwj(), "DESC" /* DESC */);
|
|
93
|
+
}
|
|
94
|
+
/** Filter to rows where this attribute has no value. */
|
|
95
|
+
isNull() {
|
|
96
|
+
return new IsNullOperation(this.cwj());
|
|
97
|
+
}
|
|
98
|
+
/** Filter to rows where this attribute has a value. */
|
|
99
|
+
isNotNull() {
|
|
100
|
+
return new IsNotNullOperation(this.cwj());
|
|
101
|
+
}
|
|
102
|
+
/** Wrap a value as a constant of this attribute's type. Overridden by the typed attributes. */
|
|
103
|
+
toConstant(_value) {
|
|
104
|
+
throw new TypeError(`${this.constructor.name} does not support isIn / isNotIn`);
|
|
105
|
+
}
|
|
106
|
+
_constants(values) {
|
|
107
|
+
const constants = values.map((v) => this.toConstant(v));
|
|
108
|
+
if (constants.length === 0) throw new Error("isIn / isNotIn need at least one value");
|
|
109
|
+
return constants;
|
|
110
|
+
}
|
|
111
|
+
/** Filter to rows where this attribute equals any of `values`. */
|
|
112
|
+
isIn(values) {
|
|
113
|
+
return new InOperation(this.cwj(), this._constants(values));
|
|
114
|
+
}
|
|
115
|
+
/** Filter to rows where this attribute equals none of `values`. Rows with no value are excluded, as in SQL. */
|
|
116
|
+
isNotIn(values) {
|
|
117
|
+
return new NotInOperation(this.cwj(), this._constants(values));
|
|
118
|
+
}
|
|
119
|
+
_windowSpec(partitionBy, orderBy) {
|
|
120
|
+
const pbArr = partitionBy === void 0 ? [] : Array.isArray(partitionBy) ? partitionBy : [partitionBy];
|
|
121
|
+
const pb = pbArr.map((x) => x instanceof _Attribute ? x.cwj() : x);
|
|
122
|
+
const ob = orderBy === void 0 ? [] : Array.isArray(orderBy) ? orderBy : [orderBy];
|
|
123
|
+
return new WindowSpecification(pb, ob);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Assign a rank to each row within the ordered window, following pandas `Series.rank` conventions.
|
|
127
|
+
*
|
|
128
|
+
* `method` controls tie-breaking (default `'min'`):
|
|
129
|
+
* - `'min'` — tied rows share the lowest rank; the next rank skips.
|
|
130
|
+
* - `'dense'` — like `'min'` but ranks are contiguous (no gaps).
|
|
131
|
+
* - `'first'` — each row gets a unique sequential integer (no ties).
|
|
132
|
+
* - `'max'` — only meaningful with `pct: true` (see below).
|
|
133
|
+
*
|
|
134
|
+
* When `pct: true` the result is a relative rank between 0 and 1:
|
|
135
|
+
* - `method: 'min'` (default) → fraction of values strictly below this row (PERCENT_RANK).
|
|
136
|
+
* - `method: 'max'` → fraction of values at or below this row (cumulative distribution).
|
|
137
|
+
*
|
|
138
|
+
* `partitionBy` restarts the ranking within each group of distinct values.
|
|
139
|
+
*/
|
|
140
|
+
rank(opts = {}) {
|
|
141
|
+
const { method = "min", pct = false, partitionBy, orderBy } = opts;
|
|
142
|
+
let func;
|
|
143
|
+
let name;
|
|
144
|
+
if (pct) {
|
|
145
|
+
if (method === "max") {
|
|
146
|
+
func = "CUME_DIST" /* CUME_DIST */;
|
|
147
|
+
name = "Cume Dist";
|
|
148
|
+
} else {
|
|
149
|
+
func = "PERCENT_RANK" /* PERCENT_RANK */;
|
|
150
|
+
name = "Percent Rank";
|
|
151
|
+
}
|
|
152
|
+
} else if (method === "first") {
|
|
153
|
+
func = "ROW_NUMBER" /* ROW_NUMBER */;
|
|
154
|
+
name = "Row Number";
|
|
155
|
+
} else if (method === "dense") {
|
|
156
|
+
func = "DENSE_RANK" /* DENSE_RANK */;
|
|
157
|
+
name = "Dense Rank";
|
|
158
|
+
} else {
|
|
159
|
+
func = "RANK" /* RANK */;
|
|
160
|
+
name = "Rank";
|
|
161
|
+
}
|
|
162
|
+
return new WindowFunctionOperation(null, func, name, void 0, [], this._windowSpec(partitionBy, orderBy));
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Split rows into `buckets` equal-sized quantile bins ordered by `orderBy`.
|
|
166
|
+
* Each row receives a bucket number from 1 (lowest) to `buckets` (highest).
|
|
167
|
+
* Named after `pandas.qcut`.
|
|
168
|
+
*/
|
|
169
|
+
qcut(buckets, opts = {}) {
|
|
170
|
+
return new WindowFunctionOperation(
|
|
171
|
+
null,
|
|
172
|
+
"NTILE" /* NTILE */,
|
|
173
|
+
"Quantile",
|
|
174
|
+
buckets,
|
|
175
|
+
[],
|
|
176
|
+
this._windowSpec(opts.partitionBy, opts.orderBy)
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Return the value of this attribute from `periods` rows earlier (positive) or later (negative)
|
|
181
|
+
* within the ordered window, following `pandas.Series.shift`.
|
|
182
|
+
* Rows with no neighbour at the given offset return `null` (or `fillValue` if provided).
|
|
183
|
+
*/
|
|
184
|
+
shift(periods = 1, opts = {}) {
|
|
185
|
+
const extraArgs = opts.fillValue !== void 0 ? [opts.fillValue] : [];
|
|
186
|
+
const func = periods >= 0 ? "LAG" /* LAG */ : "LEAD" /* LEAD */;
|
|
187
|
+
const label = periods >= 0 ? "Lag" : "Lead";
|
|
188
|
+
return new WindowFunctionOperation(
|
|
189
|
+
this.cwj(),
|
|
190
|
+
func,
|
|
191
|
+
label + " " + this._displayName,
|
|
192
|
+
Math.abs(periods),
|
|
193
|
+
extraArgs,
|
|
194
|
+
this._windowSpec(opts.partitionBy, opts.orderBy)
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Return the first value of this attribute in the window frame.
|
|
199
|
+
* Use `partitionBy` to restart the frame for each group.
|
|
200
|
+
*/
|
|
201
|
+
first(opts = {}) {
|
|
202
|
+
return new WindowFunctionOperation(
|
|
203
|
+
this.cwj(),
|
|
204
|
+
"FIRST_VALUE" /* FIRST_VALUE */,
|
|
205
|
+
"First " + this._displayName,
|
|
206
|
+
void 0,
|
|
207
|
+
[],
|
|
208
|
+
this._windowSpec(opts.partitionBy, opts.orderBy)
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Return the last value of this attribute in the window frame.
|
|
213
|
+
* Use `partitionBy` to restart the frame for each group.
|
|
214
|
+
*/
|
|
215
|
+
last(opts = {}) {
|
|
216
|
+
return new WindowFunctionOperation(
|
|
217
|
+
this.cwj(),
|
|
218
|
+
"LAST_VALUE" /* LAST_VALUE */,
|
|
219
|
+
"Last " + this._displayName,
|
|
220
|
+
void 0,
|
|
221
|
+
[],
|
|
222
|
+
this._windowSpec(opts.partitionBy, opts.orderBy)
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
export {
|
|
228
|
+
Attribute
|
|
229
|
+
};
|
|
230
|
+
//# sourceMappingURL=chunk-BGEX377Q.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/datafinder/attribute.ts"],"sourcesContent":["import {\n Column,\n AggregateOperation,\n AggregateOperator,\n ColumnWithJoin,\n SortOperation,\n SortDirection,\n IsNullOperation,\n IsNotNullOperation,\n InOperation,\n NotInOperation,\n ConstantOperation,\n JoinTreeNodeOperation,\n Alias,\n RelationalOperationElement,\n Aliasable,\n ScalarFunction,\n ScalarFunctionOperation,\n WindowFunction,\n WindowFunctionOperation,\n WindowSpecification,\n} from '../model/relational';\n\n/** A value an attribute can be compared with in `isIn` / `isNotIn`. */\nexport type AttributeValue = string | number | boolean | Date;\n\nexport abstract class Attribute implements Aliasable {\n private readonly _displayName: string;\n private readonly _column: Column;\n private readonly _owner: string;\n private readonly _parent?: JoinTreeNodeOperation;\n private readonly _embedded?: Attribute;\n private readonly _optional: boolean;\n\n constructor(\n displayName: string,\n columnName: string,\n columnDbType: string,\n owner: string,\n parent?: JoinTreeNodeOperation,\n embedded?: Attribute,\n nullable: boolean = true,\n optional: boolean = false,\n ) {\n this._optional = optional;\n this._displayName = displayName;\n this._column = new Column(columnName, columnDbType, owner, false, nullable);\n this._owner = owner;\n this._parent = parent;\n this._embedded = embedded;\n }\n\n column(): Column { return this._column; }\n owner(): string { return this._owner; }\n parent(): JoinTreeNodeOperation | undefined { return this._parent; }\n displayName(): string { return this._displayName; }\n\n /** Alternate attribute on a flat column of the owning table, used in place of this one when its join is elided. */\n embedded(): Attribute | undefined { return this._embedded; }\n\n /** @internal The expression a function of this attribute applies to: its column. */\n functionInput(): RelationalOperationElement {\n return this.cwj();\n }\n\n /** @internal What a function of this attribute is labelled after: its display name. */\n functionLabel(): string {\n return this._displayName;\n }\n\n /** @internal This attribute as an operand of arithmetic: its column. */\n arithmeticOperand(): RelationalOperationElement {\n return this.cwj();\n }\n\n cwj(): ColumnWithJoin {\n return new ColumnWithJoin(this._column, this._parent, this._embedded?.cwj(), this._optional);\n }\n\n /** Count the number of non-null values. Use in a projection with `groupBy` to aggregate per group. */\n count(): AggregateOperation {\n return new AggregateOperation(\n this.cwj(),\n AggregateOperator.COUNT,\n this._displayName + ' Count',\n );\n }\n\n /** Name this column in the result: `f.price().sum().alias('Total')`. */\n alias(name: string): Alias {\n return new Alias(this, name);\n }\n\n /** The number of different non-NULL values in the group: `COUNT(DISTINCT column)`. Use with `groupBy` to count per group. */\n countDistinct(): AggregateOperation {\n return new AggregateOperation(this.cwj(), AggregateOperator.COUNT_DISTINCT, this._displayName + ' Count Distinct');\n }\n\n /** The value, or `defaultValue` when it is NULL: `name().orElse('N/A')`. Like `COALESCE` in SQL. */\n orElse(defaultValue: string | number | boolean): ScalarFunctionOperation {\n if (!['string', 'number', 'boolean'].includes(typeof defaultValue)) {\n throw new TypeError(`An orElse default is a string, number or boolean, not ${defaultValue === null ? 'null' : typeof defaultValue}`);\n }\n return new ScalarFunctionOperation(this.cwj(), ScalarFunction.COALESCE, 'Or Else ' + this._displayName, undefined, [defaultValue]);\n }\n\n /** Sort this attribute in ascending order. Pass to `orderBy` or a window function's `orderBy`. */\n ascending(): SortOperation {\n return new SortOperation(this.cwj(), SortDirection.ASC);\n }\n\n /** Sort this attribute in descending order. Pass to `orderBy` or a window function's `orderBy`. */\n descending(): SortOperation {\n return new SortOperation(this.cwj(), SortDirection.DESC);\n }\n\n /** Filter to rows where this attribute has no value. */\n isNull(): IsNullOperation {\n return new IsNullOperation(this.cwj());\n }\n\n /** Filter to rows where this attribute has a value. */\n isNotNull(): IsNotNullOperation {\n return new IsNotNullOperation(this.cwj());\n }\n\n /** Wrap a value as a constant of this attribute's type. Overridden by the typed attributes. */\n protected toConstant(_value: AttributeValue): ConstantOperation {\n throw new TypeError(`${this.constructor.name} does not support isIn / isNotIn`);\n }\n\n private _constants(values: readonly AttributeValue[]): ConstantOperation[] {\n const constants = values.map(v => this.toConstant(v));\n if (constants.length === 0) throw new Error('isIn / isNotIn need at least one value');\n return constants;\n }\n\n /** Filter to rows where this attribute equals any of `values`. */\n isIn(values: readonly AttributeValue[]): InOperation {\n return new InOperation(this.cwj(), this._constants(values));\n }\n\n /** Filter to rows where this attribute equals none of `values`. Rows with no value are excluded, as in SQL. */\n isNotIn(values: readonly AttributeValue[]): NotInOperation {\n return new NotInOperation(this.cwj(), this._constants(values));\n }\n\n private _windowSpec(\n partitionBy?: Attribute | ColumnWithJoin | (Attribute | ColumnWithJoin)[],\n orderBy?: SortOperation | SortOperation[],\n ): WindowSpecification {\n const pbArr = partitionBy === undefined ? [] : Array.isArray(partitionBy) ? partitionBy : [partitionBy];\n const pb = pbArr.map(x => x instanceof Attribute ? x.cwj() : x);\n const ob = orderBy === undefined ? [] : Array.isArray(orderBy) ? orderBy : [orderBy];\n return new WindowSpecification(pb, ob);\n }\n\n /**\n * Assign a rank to each row within the ordered window, following pandas `Series.rank` conventions.\n *\n * `method` controls tie-breaking (default `'min'`):\n * - `'min'` — tied rows share the lowest rank; the next rank skips.\n * - `'dense'` — like `'min'` but ranks are contiguous (no gaps).\n * - `'first'` — each row gets a unique sequential integer (no ties).\n * - `'max'` — only meaningful with `pct: true` (see below).\n *\n * When `pct: true` the result is a relative rank between 0 and 1:\n * - `method: 'min'` (default) → fraction of values strictly below this row (PERCENT_RANK).\n * - `method: 'max'` → fraction of values at or below this row (cumulative distribution).\n *\n * `partitionBy` restarts the ranking within each group of distinct values.\n */\n rank(opts: {\n method?: 'min' | 'dense' | 'first' | 'max';\n pct?: boolean;\n partitionBy?: Attribute | ColumnWithJoin | (Attribute | ColumnWithJoin)[];\n orderBy?: SortOperation | SortOperation[];\n } = {}): WindowFunctionOperation {\n const { method = 'min', pct = false, partitionBy, orderBy } = opts;\n let func: WindowFunction;\n let name: string;\n if (pct) {\n if (method === 'max') { func = WindowFunction.CUME_DIST; name = 'Cume Dist'; }\n else { func = WindowFunction.PERCENT_RANK; name = 'Percent Rank'; }\n } else if (method === 'first') { func = WindowFunction.ROW_NUMBER; name = 'Row Number'; }\n else if (method === 'dense') { func = WindowFunction.DENSE_RANK; name = 'Dense Rank'; }\n else { func = WindowFunction.RANK; name = 'Rank'; }\n return new WindowFunctionOperation(null, func, name, undefined, [], this._windowSpec(partitionBy, orderBy));\n }\n\n /**\n * Split rows into `buckets` equal-sized quantile bins ordered by `orderBy`.\n * Each row receives a bucket number from 1 (lowest) to `buckets` (highest).\n * Named after `pandas.qcut`.\n */\n qcut(buckets: number, opts: {\n partitionBy?: Attribute | ColumnWithJoin | (Attribute | ColumnWithJoin)[];\n orderBy?: SortOperation | SortOperation[];\n } = {}): WindowFunctionOperation {\n return new WindowFunctionOperation(null, WindowFunction.NTILE, 'Quantile', buckets, [],\n this._windowSpec(opts.partitionBy, opts.orderBy));\n }\n\n /**\n * Return the value of this attribute from `periods` rows earlier (positive) or later (negative)\n * within the ordered window, following `pandas.Series.shift`.\n * Rows with no neighbour at the given offset return `null` (or `fillValue` if provided).\n */\n shift(periods = 1, opts: {\n fillValue?: string | number;\n partitionBy?: Attribute | ColumnWithJoin | (Attribute | ColumnWithJoin)[];\n orderBy?: SortOperation | SortOperation[];\n } = {}): WindowFunctionOperation {\n const extraArgs: (string | number)[] = opts.fillValue !== undefined ? [opts.fillValue] : [];\n const func = periods >= 0 ? WindowFunction.LAG : WindowFunction.LEAD;\n const label = periods >= 0 ? 'Lag' : 'Lead';\n return new WindowFunctionOperation(\n this.cwj(),\n func,\n label + ' ' + this._displayName,\n Math.abs(periods),\n extraArgs,\n this._windowSpec(opts.partitionBy, opts.orderBy),\n );\n }\n\n /**\n * Return the first value of this attribute in the window frame.\n * Use `partitionBy` to restart the frame for each group.\n */\n first(opts: {\n partitionBy?: Attribute | ColumnWithJoin | (Attribute | ColumnWithJoin)[];\n orderBy?: SortOperation | SortOperation[];\n } = {}): WindowFunctionOperation {\n return new WindowFunctionOperation(\n this.cwj(),\n WindowFunction.FIRST_VALUE,\n 'First ' + this._displayName,\n undefined, [],\n this._windowSpec(opts.partitionBy, opts.orderBy),\n );\n }\n\n /**\n * Return the last value of this attribute in the window frame.\n * Use `partitionBy` to restart the frame for each group.\n */\n last(opts: {\n partitionBy?: Attribute | ColumnWithJoin | (Attribute | ColumnWithJoin)[];\n orderBy?: SortOperation | SortOperation[];\n } = {}): WindowFunctionOperation {\n return new WindowFunctionOperation(\n this.cwj(),\n WindowFunction.LAST_VALUE,\n 'Last ' + this._displayName,\n undefined, [],\n this._windowSpec(opts.partitionBy, opts.orderBy),\n );\n }\n}\n"],"mappings":"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","names":[]}
|