@jarenjs/linq 0.46.5 → 0.49.2
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 +7 -0
- package/docs/LINQ-FORMAT.md +3 -1
- package/package.json +3 -3
- package/src/expression.js +95 -3
- package/types/index.d.ts +160 -17
package/README.md
CHANGED
|
@@ -52,6 +52,13 @@ played by the provider seam below.
|
|
|
52
52
|
spatial measurements are `geoArea`/`geoLength` because `length` on
|
|
53
53
|
this surface is already `$string-length` — the mapping table says so
|
|
54
54
|
in its own row.
|
|
55
|
+
- **Time is spellable.** The whole RFC 3339 date family is on the surface —
|
|
56
|
+
`e.on.startOf('month')`, `e.on.dateAdd(3, 'day')`, `e.on.week()` — and so are
|
|
57
|
+
the five time-series operators: `rows.all().resample({ every: 'PT1H', fill:
|
|
58
|
+
'locf' })`, `rows.all().rolling({ width: 60000 })` and
|
|
59
|
+
`left.all().asof(right, { by: '$.symbol' })`. A series spec is a literal and
|
|
60
|
+
is embedded verbatim, so every rule about what it may say stays in the
|
|
61
|
+
compiler rather than being restated here.
|
|
55
62
|
- **Meaning is spellable too.** `m.embedding.similarity(query)` emits
|
|
56
63
|
§8.15's `$similarity`, and k-nearest is the chain it already looks
|
|
57
64
|
like — `.orderByDescending(..., { empty: 'least' }).thenBy(m => m.id)
|
package/docs/LINQ-FORMAT.md
CHANGED
|
@@ -114,7 +114,9 @@ is part of THIS design.
|
|
|
114
114
|
| `OfType<S>` | `$valid` filter with a JSON Schema literal | native | `(schema)` → `Seq<S>`; needs `compileTypeTest` (`JL0003`) |
|
|
115
115
|
| `Cast<S>` | `$assert` per item | native | `(schema)` → `Seq<S>`; needs `compileTypeTest` (`JL0003`) |
|
|
116
116
|
| `Zip` | — no positional co-iteration in the grammar | unsupported (`JL0006`) | — |
|
|
117
|
-
| expression methods | `eq ne lt le gt ge` → `$eq…$ge`; `and or not`; `add sub mul div idiv mod neg`; `startsWith endsWith contains matches upper lower length concat substring replace` → §8.7; `count sum avg min max` → §8.8 (aggregates as expressions, e.g. over a group); `
|
|
117
|
+
| expression methods | `eq ne lt le gt ge` → `$eq…$ge`; `and or not`; `add sub mul div idiv mod neg`; `startsWith endsWith contains matches upper lower length concat substring replace` → §8.7; `count sum avg min max` → §8.8 (aggregates as expressions, e.g. over a group); `exists isEmpty`; `at all get` | native | on `Expr<…>`, per the typed-surface order |
|
|
118
|
+
| date family (§8.13) | the whole family, one method per operator. Components `year month day hours minutes seconds offset week weekYear quarter weekday`; instants `epoch datetime`; predicates `isDate isTime isDatetime isDuration`; arithmetic `startOf(unit) endOf(unit) dateAdd(duration \| amount, unit?) dateSub(…) dateDiff(to, unit) dateFormat(pattern)`. `dateAdd`/`dateSub`/`dateFormat` carry the prefix because `add`, `sub` and `format` are taken or ambiguous on this surface — the same reason §8.14 spells `geoArea`. There is no `now()`: §8.13 has no clock, and a fluent surface does not get to add one | native | on `DateTimeExpr` (the `DateTime` brand) and on `UnknownExpr` |
|
|
119
|
+
| series family (§8.16) | `overlaps(other)` → `$overlaps`; `timeBucket(every, origin?, context?)` → `$time-bucket`; `resample(spec)`, `rolling(spec)` and `asof(right, spec?)` → the three sequence operators. A **spec is a literal** and is embedded verbatim — it is read once when the query compiles, so a spec built from the row is `JL0005`, and every rule about what it may *say* stays in the compiler (`JQ0003`). Note that a member literally named `at` is read with `get('at')`: `at(index)` is path navigation on this surface | native | on `ArrayExpr`/fanned paths for the three sequence operators, on `Expr<…>` for the two scalar ones |
|
|
118
120
|
| spatial family (§8.14) | `bbox geoArea geoLength centroid` → `$bbox $area $length $centroid`; `distance within bboxIntersects` → `$distance $within $bbox-intersects`; `geohash(precision?)` → `$geohash` (optional arity, like `substring`); `geoParse geoText geohashBounds geohashNeighbours` → the conversion family; `geoSimplify(tolerance)` → `$geo-simplify`. A plain JSON polygon embeds as a literal (`p.at.within(poly)`); `.params({ region })` makes it an external instead | native | on `Expr<…>`, per the typed-surface order |
|
|
119
121
|
| vector family (§8.15) | `similarity(other)` → `$similarity`. The other operand is an array of numbers: a captured one embeds as a literal, `.params({ query })` binds it at call time. There is no `knn` method — k-nearest is `orderByDescending(...).take(k)`, which is the composition the emitted document already is | native | on `Expr<…>`, per the typed-surface order |
|
|
120
122
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jarenjs/linq",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.49.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
7
7
|
"types": "./types/index.d.ts",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"prepack": "npm run build:types"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@jarenjs/core": "^0.
|
|
51
|
-
"@jarenjs/json": "^0.
|
|
50
|
+
"@jarenjs/core": "^0.49.2",
|
|
51
|
+
"@jarenjs/json": "^0.49.2"
|
|
52
52
|
}
|
|
53
53
|
}
|
package/src/expression.js
CHANGED
|
@@ -171,6 +171,43 @@ const unary = (op) => function (/** @type {any} */ record) {
|
|
|
171
171
|
return makeExpr({ [op]: record.doc }, record.epoch, false);
|
|
172
172
|
};
|
|
173
173
|
|
|
174
|
+
/**
|
|
175
|
+
* `$date-add` / `$date-sub`: `[date, duration]` or `[date, amount, unit]`.
|
|
176
|
+
* @param {any} record
|
|
177
|
+
* @param {any} amount - an ISO 8601 duration, or a number of units
|
|
178
|
+
* @param {any} [unit] - the calendar unit, when `amount` is a number
|
|
179
|
+
* @returns {any[]}
|
|
180
|
+
*/
|
|
181
|
+
function shiftArgs(record, amount, unit) {
|
|
182
|
+
return unit === undefined
|
|
183
|
+
? [record.doc, toExpression(amount)]
|
|
184
|
+
: [record.doc, toExpression(amount), toExpression(unit)];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* A §8.16 spec: captured data, embedded verbatim.
|
|
189
|
+
*
|
|
190
|
+
* These are the one place this surface hands the compiler something it
|
|
191
|
+
* must NOT evaluate — a width, an aggregate, a fill policy and a row
|
|
192
|
+
* selector are read once when the query compiles, which is what makes
|
|
193
|
+
* them checkable at all. `toExpression` would turn `{ every: 'PT1H' }`
|
|
194
|
+
* into a map constructor and `{ at: '$.on' }` into a path; the spec is
|
|
195
|
+
* therefore embedded as it was written, and every rule about what it may
|
|
196
|
+
* contain stays where it already is, in the query compiler (`JQ0003`).
|
|
197
|
+
*
|
|
198
|
+
* @param {any} spec
|
|
199
|
+
* @param {string} method - for the message
|
|
200
|
+
* @returns {any} the spec, verbatim
|
|
201
|
+
*/
|
|
202
|
+
function literalSpec(spec, method) {
|
|
203
|
+
if (!isPlainJson(spec) || spec === null || typeof spec !== 'object' || Array.isArray(spec)) {
|
|
204
|
+
throw new LinqBuildError('JL0005',
|
|
205
|
+
`${method}() takes a plain literal spec object; it is read once when the query`
|
|
206
|
+
+ ' compiles, so it cannot be an expression or carry a captured value');
|
|
207
|
+
}
|
|
208
|
+
return spec;
|
|
209
|
+
}
|
|
210
|
+
|
|
174
211
|
/**
|
|
175
212
|
* The operator methods, name → builder(record, ...args). One table so
|
|
176
213
|
* the mapping in LINQ-FORMAT.md §4 has exactly one code counterpart.
|
|
@@ -212,10 +249,65 @@ const METHODS = {
|
|
|
212
249
|
// `(u, g) => ({ n: g.count() })`)
|
|
213
250
|
count: unary('$count'), sum: unary('$sum'), avg: unary('$avg'),
|
|
214
251
|
min: unary('$min'), max: unary('$max'),
|
|
215
|
-
// §8.13 dates
|
|
216
|
-
//
|
|
252
|
+
// §8.13 dates — the whole family, not a corner of it. A date in this
|
|
253
|
+
// suite is an RFC 3339 STRING, so every one of these is an ordinary
|
|
254
|
+
// string operator with a calendar's worth of rules behind it, and
|
|
255
|
+
// every one lowers to the operator of the same name: there is no
|
|
256
|
+
// LINQ-only date semantics to learn and nothing here a hand-written
|
|
257
|
+
// document could not have said.
|
|
258
|
+
//
|
|
259
|
+
// `dateFormat` rather than `format`, and `dateAdd`/`dateSub` rather
|
|
260
|
+
// than `add`/`sub`, because `add` is already `$add` on this surface —
|
|
261
|
+
// the same reason §8.14 spells `geoArea`. Where no method name is
|
|
262
|
+
// taken, the operator's own name is used unprefixed (`week`,
|
|
263
|
+
// `quarter`, `startOf`).
|
|
217
264
|
year: unary('$year'), month: unary('$month'), day: unary('$day'),
|
|
218
|
-
|
|
265
|
+
hours: unary('$hours'), minutes: unary('$minutes'), seconds: unary('$seconds'),
|
|
266
|
+
offset: unary('$offset'), epoch: unary('$epoch'), datetime: unary('$datetime'),
|
|
267
|
+
week: unary('$week'), weekYear: unary('$week-year'),
|
|
268
|
+
quarter: unary('$quarter'), weekday: unary('$weekday'),
|
|
269
|
+
isDate: unary('$is-date'), isTime: unary('$is-time'),
|
|
270
|
+
isDatetime: unary('$is-datetime'), isDuration: unary('$is-duration'),
|
|
271
|
+
startOf: binary('$start-of'), endOf: binary('$end-of'),
|
|
272
|
+
dateFormat: binary('$date-format'),
|
|
273
|
+
dateAdd(record, amount, unit) {
|
|
274
|
+
return makeExpr({ '$date-add': shiftArgs(record, amount, unit) }, record.epoch, false);
|
|
275
|
+
},
|
|
276
|
+
dateSub(record, amount, unit) {
|
|
277
|
+
return makeExpr({ '$date-sub': shiftArgs(record, amount, unit) }, record.epoch, false);
|
|
278
|
+
},
|
|
279
|
+
dateDiff(record, to, unit) {
|
|
280
|
+
return makeExpr(
|
|
281
|
+
{ '$date-diff': [record.doc, toExpression(to), toExpression(unit)] },
|
|
282
|
+
record.epoch, false);
|
|
283
|
+
},
|
|
284
|
+
// §8.16 time series. The three sequence-valued operators take a
|
|
285
|
+
// VERBATIM spec literal, so the argument is embedded with `$const`'s
|
|
286
|
+
// discipline - it is captured data, never an expression - and the
|
|
287
|
+
// compiler owns every rule about what it may say.
|
|
288
|
+
overlaps: binary('$overlaps'),
|
|
289
|
+
timeBucket(record, every, origin, context) {
|
|
290
|
+
const args = [record.doc, toExpression(every)];
|
|
291
|
+
if (origin !== undefined || context !== undefined)
|
|
292
|
+
args.push(origin === undefined ? null : toExpression(origin));
|
|
293
|
+
if (context !== undefined)
|
|
294
|
+
args.push(literalSpec(context, 'timeBucket'));
|
|
295
|
+
return makeExpr({ '$time-bucket': args }, record.epoch, false);
|
|
296
|
+
},
|
|
297
|
+
resample(record, spec) {
|
|
298
|
+
return makeExpr({ $resample: [record.doc, literalSpec(spec, 'resample')] },
|
|
299
|
+
record.epoch, false);
|
|
300
|
+
},
|
|
301
|
+
rolling(record, spec) {
|
|
302
|
+
return makeExpr({ $rolling: [record.doc, literalSpec(spec, 'rolling')] },
|
|
303
|
+
record.epoch, false);
|
|
304
|
+
},
|
|
305
|
+
asof(record, right, spec) {
|
|
306
|
+
const args = [record.doc, toExpression(right)];
|
|
307
|
+
if (spec !== undefined)
|
|
308
|
+
args.push(literalSpec(spec, 'asof'));
|
|
309
|
+
return makeExpr({ $asof: args }, record.epoch, false);
|
|
310
|
+
},
|
|
219
311
|
// §8.14 spatial. `geoArea`/`geoLength` rather than `area`/`length`:
|
|
220
312
|
// `length` is already `$string-length` on this surface and renaming a
|
|
221
313
|
// shipped method for symmetry is a breaking change for a cosmetic
|
package/types/index.d.ts
CHANGED
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* The nominal date-time brand: annotate a model property as `DateTime`
|
|
17
|
-
* and the date
|
|
18
|
-
*
|
|
19
|
-
* date. Purely a type-level marker — the runtime value is a plain
|
|
17
|
+
* and the whole §8.13 date family (`year()`, `startOf()`, `dateAdd()`,
|
|
18
|
+
* `timeBucket()`, …) becomes available on its expression without making
|
|
19
|
+
* EVERY string a date. Purely a type-level marker — the runtime value is a plain
|
|
20
20
|
* RFC 3339 string; there is no constructor and no runtime cost.
|
|
21
21
|
*/
|
|
22
22
|
export type DateTime = string & { readonly __jarenTag: 'date-time' };
|
|
@@ -59,6 +59,10 @@ export interface NumberExpr extends ExprBase<number>, EqExpr<number> {
|
|
|
59
59
|
idiv(value: number | NumberExpr): NumberExpr;
|
|
60
60
|
mod(value: number | NumberExpr): NumberExpr;
|
|
61
61
|
neg(): NumberExpr;
|
|
62
|
+
/** An epoch is a number, so the two instant operators live here too. */
|
|
63
|
+
datetime(): DateTimeExpr;
|
|
64
|
+
timeBucket(every: string | number, origin?: string | number | null,
|
|
65
|
+
context?: CalendarContext): NumberExpr;
|
|
62
66
|
}
|
|
63
67
|
|
|
64
68
|
export interface StringExpr extends ExprBase<string>, EqExpr<string> {
|
|
@@ -79,25 +83,165 @@ export interface StringExpr extends ExprBase<string>, EqExpr<string> {
|
|
|
79
83
|
replace(pattern: string, replacement: string): StringExpr;
|
|
80
84
|
}
|
|
81
85
|
|
|
82
|
-
/** A
|
|
83
|
-
*
|
|
84
|
-
|
|
86
|
+
/** A calendar unit, as QUERY-FORMAT §8.13 fixes it. The unit is DATA
|
|
87
|
+
* rather than vocabulary, so an unknown one is a runtime `JQ2001`; this
|
|
88
|
+
* type is what keeps the common spelling mistake a compile error. */
|
|
89
|
+
export type DateUnit =
|
|
90
|
+
| 'year' | 'quarter' | 'month' | 'week' | 'day'
|
|
91
|
+
| 'hour' | 'minute' | 'second' | 'millisecond';
|
|
92
|
+
|
|
93
|
+
/** The date family (`$is-date` … `$date-format`), available on any
|
|
94
|
+
* expression whose value carries a date. Every method lowers to the §8.13
|
|
95
|
+
* operator of the same name; there is no LINQ-only date semantics. */
|
|
96
|
+
export interface DateMethods {
|
|
97
|
+
/** Lexical date components; a value with no date half is `JQ2001`. */
|
|
98
|
+
year(): NumberExpr;
|
|
99
|
+
month(): NumberExpr;
|
|
100
|
+
day(): NumberExpr;
|
|
101
|
+
/** Lexical time components; `seconds` carries its fraction. */
|
|
102
|
+
hours(): NumberExpr;
|
|
103
|
+
minutes(): NumberExpr;
|
|
104
|
+
seconds(): NumberExpr;
|
|
105
|
+
/** Minutes east of UTC; a bare `full-date` yields the empty sequence. */
|
|
106
|
+
offset(): NumberExpr;
|
|
107
|
+
/** ISO 8601 week number, and its week-numbering year. */
|
|
108
|
+
week(): NumberExpr;
|
|
109
|
+
weekYear(): NumberExpr;
|
|
110
|
+
/** Calendar quarter 1-4; ISO weekday 1 (Monday) to 7 (Sunday). */
|
|
111
|
+
quarter(): NumberExpr;
|
|
112
|
+
weekday(): NumberExpr;
|
|
113
|
+
/** Epoch milliseconds (`$epoch`) — the one shift to UTC. */
|
|
114
|
+
epoch(): NumberExpr;
|
|
115
|
+
/** The inverse: epoch milliseconds → a canonical UTC `date-time`. */
|
|
116
|
+
datetime(): DateTimeExpr;
|
|
117
|
+
/** The RFC 3339 lexical-form predicates; these never raise. */
|
|
118
|
+
isDate(): BoolExpr;
|
|
119
|
+
isTime(): BoolExpr;
|
|
120
|
+
isDatetime(): BoolExpr;
|
|
121
|
+
isDuration(): BoolExpr;
|
|
122
|
+
/** Truncate to a unit, keeping the lexical form (`$start-of`/`$end-of`). */
|
|
123
|
+
startOf(unit: DateUnit): DateTimeExpr;
|
|
124
|
+
endOf(unit: DateUnit): DateTimeExpr;
|
|
125
|
+
/** Shift by an ISO 8601 duration, or by an amount and a unit. */
|
|
126
|
+
dateAdd(duration: string): DateTimeExpr;
|
|
127
|
+
dateAdd(amount: number | NumberExpr, unit: DateUnit): DateTimeExpr;
|
|
128
|
+
dateSub(duration: string): DateTimeExpr;
|
|
129
|
+
dateSub(amount: number | NumberExpr, unit: DateUnit): DateTimeExpr;
|
|
130
|
+
/** Whole units from this value to another; negative when it precedes. */
|
|
131
|
+
dateDiff(to: string | DateTimeExpr, unit: DateUnit): NumberExpr;
|
|
132
|
+
/** Render through a Unicode LDML pattern (`yyyy-MM-dd`). */
|
|
133
|
+
dateFormat(pattern: string): StringExpr;
|
|
134
|
+
/** The instant labelling the bucket this one falls in (`$time-bucket`). */
|
|
135
|
+
timeBucket(every: string | number, origin?: string | number | null,
|
|
136
|
+
context?: CalendarContext): NumberExpr;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** A `DateTime`-branded string: the string surface plus the whole §8.13
|
|
140
|
+
* date family. */
|
|
141
|
+
export interface DateTimeExpr extends ExprBase<DateTime>, DateMethods {
|
|
85
142
|
eq(value: string | DateTimeExpr | null): BoolExpr;
|
|
86
143
|
ne(value: string | DateTimeExpr | null): BoolExpr;
|
|
87
144
|
lt(value: string | DateTimeExpr): BoolExpr;
|
|
88
145
|
le(value: string | DateTimeExpr): BoolExpr;
|
|
89
146
|
gt(value: string | DateTimeExpr): BoolExpr;
|
|
90
147
|
ge(value: string | DateTimeExpr): BoolExpr;
|
|
91
|
-
year(): NumberExpr;
|
|
92
|
-
month(): NumberExpr;
|
|
93
|
-
day(): NumberExpr;
|
|
94
|
-
/** Epoch milliseconds (`$epoch`). */
|
|
95
|
-
epoch(): NumberExpr;
|
|
96
148
|
}
|
|
97
149
|
|
|
98
|
-
|
|
150
|
+
/** The wall clock a calendar boundary falls on (QUERY-FORMAT §8.16).
|
|
151
|
+
* UTC is the default; a named `zone` needs an injected `zoneProvider`. */
|
|
152
|
+
export interface CalendarContext {
|
|
153
|
+
zone?: string;
|
|
154
|
+
offset?: number;
|
|
155
|
+
disambiguation?: 'reject' | 'earlier' | 'later';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** The aggregates `$resample` and `$rolling` share. */
|
|
159
|
+
export type SeriesAggregate =
|
|
160
|
+
'sum' | 'mean' | 'min' | 'max' | 'first' | 'last' | 'count';
|
|
161
|
+
|
|
162
|
+
/** What an EMPTY bucket says, and nothing else. */
|
|
163
|
+
export type SeriesFill = 'omit' | 'null' | 'zero' | 'locf' | 'linear';
|
|
164
|
+
|
|
165
|
+
/** A width: an ISO 8601 duration, or a count of milliseconds. */
|
|
166
|
+
export type SeriesSpan = string | number;
|
|
167
|
+
|
|
168
|
+
/** An instant: epoch milliseconds, or an RFC 3339 string. */
|
|
169
|
+
export type SeriesInstant = string | number;
|
|
170
|
+
|
|
171
|
+
/** A row selector: a singular path whose `$` is the ROW rather than the
|
|
172
|
+
* document (`'$.on'`, `"$['recorded at']"`). */
|
|
173
|
+
export type RowSelector = string;
|
|
174
|
+
|
|
175
|
+
/** The `$resample` spec — a literal, read once when the query compiles. */
|
|
176
|
+
export interface ResampleSpec extends CalendarContext {
|
|
177
|
+
every: SeriesSpan;
|
|
178
|
+
origin?: SeriesInstant;
|
|
179
|
+
start?: SeriesInstant;
|
|
180
|
+
end?: SeriesInstant;
|
|
181
|
+
aggregate?: SeriesAggregate;
|
|
182
|
+
fill?: SeriesFill;
|
|
183
|
+
at?: RowSelector;
|
|
184
|
+
value?: RowSelector;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** The `$rolling` spec — a window measured in time, not in rows. */
|
|
188
|
+
export interface RollingSpec extends CalendarContext {
|
|
189
|
+
width: SeriesSpan;
|
|
190
|
+
aggregate?: SeriesAggregate;
|
|
191
|
+
minPeriods?: number;
|
|
192
|
+
at?: RowSelector;
|
|
193
|
+
value?: RowSelector;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** The `$asof` spec — every member optional: backward, unkeyed, unbounded. */
|
|
197
|
+
export interface AsOfSpec {
|
|
198
|
+
direction?: 'backward' | 'forward' | 'nearest';
|
|
199
|
+
tolerance?: SeriesSpan;
|
|
200
|
+
by?: RowSelector;
|
|
201
|
+
leftAt?: RowSelector;
|
|
202
|
+
rightAt?: RowSelector;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** One canonical sample the series operators answer with. */
|
|
206
|
+
export interface SeriesBucket {
|
|
207
|
+
at: number;
|
|
208
|
+
value: number | null;
|
|
209
|
+
count: number;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** One row of an as-of join; `right` is `null` when nothing matched, and
|
|
213
|
+
* the row stays in the answer. */
|
|
214
|
+
export interface AsOfMatch {
|
|
215
|
+
left: unknown;
|
|
216
|
+
right: unknown;
|
|
217
|
+
distance: number | null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** A half-open interval: `[start, end)`, in epoch milliseconds. */
|
|
221
|
+
export interface Interval {
|
|
222
|
+
start: SeriesInstant;
|
|
223
|
+
end: SeriesInstant;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** The §8.16 operators that take a whole series and answer another one.
|
|
227
|
+
* Available wherever a MANY-cardinality expression is (an array member,
|
|
228
|
+
* or a fanned path). */
|
|
229
|
+
export interface SeriesMethods {
|
|
230
|
+
/** Sorted `{at, value, count}` buckets, one per `every` (`$resample`). */
|
|
231
|
+
resample(spec: ResampleSpec): Expr<SeriesBucket[]> & AggregatableExpr;
|
|
232
|
+
/** One row per input instant, over a window measured in time. */
|
|
233
|
+
rolling(spec: RollingSpec): Expr<SeriesBucket[]> & AggregatableExpr;
|
|
234
|
+
/** The right row that was current when each left row happened. */
|
|
235
|
+
asof(right: ExprBase<unknown> | readonly unknown[], spec?: AsOfSpec):
|
|
236
|
+
Expr<AsOfMatch[]> & AggregatableExpr;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export interface ArrayExpr<E> extends ExprBase<E[]>, SeriesMethods {
|
|
99
240
|
eq(value: readonly E[] | ArrayExpr<E> | null): BoolExpr;
|
|
100
241
|
ne(value: readonly E[] | ArrayExpr<E> | null): BoolExpr;
|
|
242
|
+
/** Do two half-open `{start, end}` intervals share an instant?
|
|
243
|
+
* Touching spans do not. */
|
|
244
|
+
overlaps(other: Interval | ExprBase<unknown>): BoolExpr;
|
|
101
245
|
/** Fan the elements out (`[*]`) — a MANY-cardinality expression the
|
|
102
246
|
* aggregates apply to (`u.tags.all().count()`). */
|
|
103
247
|
all(): Expr<E> & AggregatableExpr;
|
|
@@ -126,7 +270,8 @@ export type ObjectExpr<T> = ExprBase<T> & EqExpr<T> & {
|
|
|
126
270
|
/** The honest top: everything is available, nothing is precise. Used
|
|
127
271
|
* where inference ends (dynamic `get`, post-operator members, unknown
|
|
128
272
|
* elements) — wide, never wrong. */
|
|
129
|
-
export interface UnknownExpr
|
|
273
|
+
export interface UnknownExpr
|
|
274
|
+
extends ExprBase<unknown>, AggregatableExpr, DateMethods, SeriesMethods {
|
|
130
275
|
eq(value: unknown): BoolExpr;
|
|
131
276
|
ne(value: unknown): BoolExpr;
|
|
132
277
|
lt(value: unknown): BoolExpr;
|
|
@@ -153,12 +298,10 @@ export interface UnknownExpr extends ExprBase<unknown>, AggregatableExpr {
|
|
|
153
298
|
concat(value: unknown): UnknownExpr;
|
|
154
299
|
substring(start: number, length?: number): UnknownExpr;
|
|
155
300
|
replace(pattern: string, replacement: string): UnknownExpr;
|
|
156
|
-
year(): NumberExpr;
|
|
157
|
-
month(): NumberExpr;
|
|
158
|
-
day(): NumberExpr;
|
|
159
|
-
epoch(): NumberExpr;
|
|
160
301
|
all(): UnknownExpr;
|
|
161
302
|
at(index: number): UnknownExpr;
|
|
303
|
+
/** Do two half-open `{start, end}` intervals share an instant? */
|
|
304
|
+
overlaps(other: Interval | ExprBase<unknown>): BoolExpr;
|
|
162
305
|
}
|
|
163
306
|
|
|
164
307
|
/** Value type → expression type. Order matters: the DateTime brand is
|