@platforma-sdk/workflow-tengo 4.5.3 → 4.6.1

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 (39) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.js +6 -6
  3. package/dist/index.mjs +6 -6
  4. package/dist/tengo/lib/pframes.bundle.lib.tengo +37 -300
  5. package/dist/tengo/lib/pframes.lib.tengo +4 -1
  6. package/dist/tengo/lib/pframes.spec.lib.tengo +21 -1
  7. package/dist/tengo/lib/pframes.xsv-builder.lib.tengo +625 -0
  8. package/dist/tengo/lib/pt.expression.lib.tengo +905 -0
  9. package/dist/tengo/lib/pt.lib.tengo +1000 -0
  10. package/dist/tengo/tpl/check_network.create_workdir_from_storage.plj.gz +0 -0
  11. package/dist/tengo/tpl/check_network.download_blob.plj.gz +0 -0
  12. package/dist/tengo/tpl/check_network.run_hello_world.plj.gz +0 -0
  13. package/dist/tengo/tpl/check_network.run_hello_world_py.plj.gz +0 -0
  14. package/dist/tengo/tpl/check_network.upload_blob.plj.gz +0 -0
  15. package/dist/tengo/tpl/check_network.upload_template.plj.gz +0 -0
  16. package/dist/tengo/tpl/exec.R.create-renv.plj.gz +0 -0
  17. package/dist/tengo/tpl/exec.exec.plj.gz +0 -0
  18. package/dist/tengo/tpl/exec.monetization_info.plj.gz +0 -0
  19. package/dist/tengo/tpl/exec.monetization_to_json.plj.gz +0 -0
  20. package/dist/tengo/tpl/exec.python.create-venv.plj.gz +0 -0
  21. package/dist/tengo/tpl/exec.python.install-deps.plj.gz +0 -0
  22. package/dist/tengo/tpl/exec.python.venv-bin-path.plj.gz +0 -0
  23. package/dist/tengo/tpl/ll.get-field.plj.gz +0 -0
  24. package/dist/tengo/tpl/pframes.export-multiple-pcolumns.plj.gz +0 -0
  25. package/dist/tengo/tpl/pframes.export-pframe-for-ui.plj.gz +0 -0
  26. package/dist/tengo/tpl/pframes.export-pframe.plj.gz +0 -0
  27. package/dist/tengo/tpl/pframes.export-single-pcolumn.plj.gz +0 -0
  28. package/dist/tengo/tpl/pframes.import-dir.plj.gz +0 -0
  29. package/dist/tengo/tpl/pframes.process-pcolumn-data.plj.gz +0 -0
  30. package/dist/tengo/tpl/pframes.remove-nulls.plj.gz +0 -0
  31. package/dist/tengo/tpl/pframes.slice-data.plj.gz +0 -0
  32. package/dist/tengo/tpl/pframes.xsv-export-pframe.plj.gz +0 -0
  33. package/dist/tengo/tpl/pframes.xsv-import-if-not-null.plj.gz +0 -0
  34. package/dist/tengo/tpl/pframes.xsv-import-map.plj.gz +0 -0
  35. package/dist/tengo/tpl/waiter.waiter.plj.gz +0 -0
  36. package/dist/tengo/tpl/workdir.save.plj.gz +0 -0
  37. package/dist/tengo/tpl/workflow.build-ctx.plj.gz +0 -0
  38. package/dist/tengo/tpl/workflow.query-anchored.plj.gz +0 -0
  39. package/package.json +3 -2
@@ -0,0 +1,905 @@
1
+ ll := import("@platforma-sdk/workflow-tengo:ll")
2
+ maps := import("@platforma-sdk/workflow-tengo:maps")
3
+ slices := import("@platforma-sdk/workflow-tengo:slices")
4
+
5
+
6
+
7
+ _newExpression := undefined
8
+ _newWindowExpression := undefined
9
+ col := undefined
10
+ lit := undefined
11
+ allHorizontal := undefined
12
+ anyHorizontal := undefined
13
+ and := undefined
14
+ or := undefined
15
+ when := undefined
16
+ rank := undefined
17
+
18
+
19
+ _isExpression := func(item) {
20
+ return ll.isMap(item) && ll.methodExists(item, "getExpression")
21
+ }
22
+
23
+
24
+ _isAggregation := func(item) {
25
+ return ll.isMap(item) && ll.methodExists(item, "getAggregation")
26
+ }
27
+
28
+
29
+ _mapToExpressionStructList := func(itemsList, stringInterpretation) {
30
+ if !is_array(itemsList) {
31
+ itemsList = [itemsList] // Allow single item to be passed
32
+ }
33
+ if stringInterpretation != "col" && stringInterpretation != "lit" {
34
+ ll.panic("Invalid stringInterpretation ('%v') for _mapToExpressionStructList. Must be 'col' or 'lit'.", stringInterpretation)
35
+ return undefined // Unreachable
36
+ }
37
+ return slices.map(itemsList, func(item) {
38
+ if is_string(item) {
39
+ if stringInterpretation == "col" {
40
+ return col(item).getExpression()
41
+ } else { // Must be "lit" due to the check above
42
+ return lit(item).getExpression()
43
+ }
44
+ } else if _isExpression(item) {
45
+ return item.getExpression()
46
+ } else {
47
+ ll.panic("Invalid item type in list: %T. Must be a string (interpreted as '%s') or an expression object.", item, stringInterpretation)
48
+ return undefined // Unreachable
49
+ }
50
+ })
51
+ }
52
+
53
+ _mapToSingleExpressionStruct := func(item, stringInterpretation) {
54
+ if _isExpression(item) {
55
+ return item.getExpression()
56
+ } else if is_int(item) || is_float(item) || is_bool(item) { // These are always literals
57
+ return lit(item).getExpression()
58
+ } else if is_string(item) {
59
+ if stringInterpretation == "col" {
60
+ return col(item).getExpression()
61
+ } else if stringInterpretation == "lit" {
62
+ return lit(item).getExpression()
63
+ } else { // stringInterpretation is undefined or an invalid value
64
+ ll.panic("String item '%v' provided, but its interpretation as 'col' or 'lit' was not specified or is not applicable in this context. (stringInterpretation: '%v')", item, stringInterpretation)
65
+ return undefined
66
+ }
67
+ } else {
68
+ ll.panic("Invalid item type: %T. Expected an expression, literal (number, bool, null), or string (with 'col'/'lit' interpretation).", item)
69
+ return undefined
70
+ }
71
+ }
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+ _newWindowExpression = func(expression, aggregation, currentAlias) {
80
+ self := undefined
81
+ self = ll.toStrict({
82
+
83
+ getExpression: func() {
84
+ if expression == undefined {
85
+ ll.panic("Using this operation as a window expression is not allowed. Use it as a group by expression instead. %v", aggregation)
86
+ }
87
+ return expression
88
+ },
89
+
90
+ getAggregation: func() {
91
+ if aggregation == undefined {
92
+ ll.panic("Using this operation as a group by expression is not allowed, most probably because you applied .over() to it. %v", expression)
93
+ }
94
+ return maps.deepMerge(aggregation, {name: self.getAlias()})
95
+ },
96
+
97
+ getAlias: func() {
98
+ if currentAlias == undefined {
99
+ ll.panic("Alias is not defined for this window expression.")
100
+ }
101
+ return currentAlias
102
+ },
103
+
104
+
105
+
106
+
107
+
108
+
109
+
110
+ alias: func(newAlias) {
111
+ return _newWindowExpression(expression, aggregation, newAlias)
112
+ },
113
+
114
+
115
+
116
+
117
+
118
+
119
+ over: func(partitionBy) {
120
+ ll.assert(is_array(partitionBy) || is_string(partitionBy) || _isExpression(partitionBy), "partitionBy must be an array of strings/expressions or a single string/expression")
121
+
122
+ if len(expression.partitionBy) > 0 {
123
+ ll.warn("partitionBy already set on this window expression, overriding.")
124
+ }
125
+
126
+ newExpression := maps.deepMerge(expression, {
127
+ partitionBy: _mapToExpressionStructList(partitionBy, "col")
128
+ })
129
+
130
+ return _newWindowExpression(newExpression, undefined, currentAlias)
131
+ }
132
+ })
133
+ return self
134
+ }
135
+
136
+
137
+
138
+
139
+
140
+
141
+ _newExpression = func(expression, currentAlias) {
142
+ self := undefined // forward declaration
143
+
144
+
145
+ binaryOp := func(opType, other) {
146
+ rhsExprStruct := undefined
147
+ if _isExpression(other) {
148
+ rhsExprStruct = other.getExpression()
149
+ } else if is_int(other) || is_float(other) || is_bool(other) || is_string(other) {
150
+ rhsExprStruct = lit(other).getExpression()
151
+ } else {
152
+ ll.panic(opType + " expects an expression object or a literal (number, boolean, null) as argument. Got type: %T", other)
153
+ }
154
+
155
+ return _newExpression(
156
+ {type: opType, lhs: expression, rhs: rhsExprStruct},
157
+ undefined // Result of operation typically doesn't carry forward alias
158
+ )
159
+ }
160
+
161
+
162
+ unaryOp := func(opType) {
163
+ return _newExpression(
164
+ {type: opType, value: expression},
165
+ undefined
166
+ )
167
+ }
168
+
169
+
170
+ unaryStrOp := func(opType) {
171
+ return _newExpression(
172
+ {type: opType, value: expression},
173
+ undefined
174
+ )
175
+ }
176
+
177
+
178
+ aggOp := func(aggType) {
179
+ return _newWindowExpression(
180
+ {type: "aggregate", aggregation: aggType, value: expression, partitionBy: []},
181
+ {aggregation: aggType, expression: expression},
182
+ undefined // Alias of underlying col, window expression itself is typically aliased later
183
+ )
184
+ }
185
+
186
+ aggOpBy := func(aggType, by) {
187
+ return _newWindowExpression(
188
+ undefined,
189
+ {aggregation: aggType, expression: expression, by: _mapToExpressionStructList(by, "col")},
190
+ undefined // Alias of underlying col, window expression itself is typically aliased later
191
+ )
192
+ }
193
+
194
+ self = ll.toStrict({
195
+
196
+ getExpression: func() { return expression },
197
+
198
+ getAlias: func() {
199
+ if currentAlias == undefined {
200
+ ll.panic("Alias is not defined for this expression.")
201
+ }
202
+ return currentAlias
203
+ },
204
+
205
+
206
+
207
+
208
+
209
+
210
+ alias: func(newAlias) {
211
+ return _newExpression(expression, newAlias)
212
+ },
213
+
214
+
215
+
216
+ gt: func(other) { return binaryOp("gt", other) },
217
+
218
+ ge: func(other) { return binaryOp("ge", other) },
219
+
220
+ eq: func(other) { return binaryOp("eq", other) },
221
+
222
+ lt: func(other) { return binaryOp("lt", other) },
223
+
224
+ le: func(other) { return binaryOp("le", other) },
225
+
226
+ neq: func(other) { return binaryOp("neq", other) },
227
+
228
+
229
+
230
+ plus: func(other) { return binaryOp("plus", other) },
231
+
232
+ minus: func(other) { return binaryOp("minus", other) },
233
+
234
+ multiply: func(other) { return binaryOp("multiply", other) },
235
+
236
+ truediv: func(other) { return binaryOp("truediv", other) },
237
+
238
+ floordiv: func(other) { return binaryOp("floordiv", other) },
239
+
240
+
241
+ log10: func() { return unaryOp("log10") },
242
+
243
+ log: func() { return unaryOp("log") },
244
+
245
+ log2: func() { return unaryOp("log2") },
246
+
247
+ abs: func() { return unaryOp("abs") },
248
+
249
+ sqrt: func() { return unaryOp("sqrt") },
250
+
251
+ negate: func() { return unaryOp("negate") },
252
+
253
+ floor: func() { return unaryOp("floor") },
254
+
255
+ round: func() { return unaryOp("round") },
256
+
257
+ ceil: func() { return unaryOp("ceil") },
258
+
259
+
260
+
261
+
262
+
263
+
264
+
265
+
266
+
267
+
268
+ cast: func(dtype, ...options) {
269
+ strictVal := false
270
+ if len(options) > 0 {
271
+ ll.assert(len(options) == 1, "cast accepts at most one options map argument.")
272
+ opsMap := options[0]
273
+ ll.assert(is_map(opsMap), "Second argument to cast(), if provided, must be an options map.")
274
+ if !is_undefined(opsMap.strict) {
275
+ ll.assert(is_bool(opsMap.strict), "strict in options map must be a boolean")
276
+ strictVal = opsMap.strict
277
+ }
278
+ }
279
+ return _newExpression(
280
+ {type: "cast", value: expression, dtype: dtype, strict: strictVal},
281
+ undefined
282
+ )
283
+ },
284
+
285
+
286
+
287
+ and: func(other) {
288
+ ll.assert(_isExpression(other), "and method expects an expression object as argument")
289
+ return allHorizontal(self, other) // Refer to top-level function
290
+ },
291
+
292
+ or: func(other) {
293
+ ll.assert(_isExpression(other), "or method expects an expression object as argument")
294
+ return anyHorizontal(self, other) // Refer to top-level function
295
+ },
296
+
297
+ not: func() {
298
+ return unaryOp("not")
299
+ },
300
+
301
+
302
+
303
+ isNull: func() { return unaryOp("is_na") }, // Polars: is_null(), TS: is_na
304
+
305
+ isNotNull: func() { return unaryOp("is_not_na") }, // Polars: is_not_null(), TS: is_not_na
306
+
307
+
308
+
309
+ strToUpper: func() { return unaryStrOp("to_upper") },
310
+
311
+ strToLower: func() { return unaryStrOp("to_lower") },
312
+
313
+ strLenChars: func() { return unaryStrOp("str_len")},
314
+
315
+
316
+
317
+
318
+
319
+
320
+
321
+ strSlice: func(start, ...lengthArgs) { // Primary method with optional length via varargs
322
+ ll.assert(is_int(start), "start must be an integer")
323
+ ll.assert(len(lengthArgs) <= 1, "strSlice expects at most one optional length argument.")
324
+
325
+ exprDetails := {type: "substring", value: expression, start: start}
326
+ if len(lengthArgs) == 1 {
327
+ lengthVal := lengthArgs[0]
328
+ ll.assert(is_int(lengthVal), "length, if provided, must be an integer")
329
+ exprDetails.length = lengthVal
330
+ }
331
+
332
+ return _newExpression(exprDetails, undefined)
333
+ },
334
+
335
+
336
+
337
+
338
+
339
+
340
+
341
+ strSliceEnd: func(start, endIdx) { // Method with end index
342
+ ll.assert(is_int(start), "start must be an integer")
343
+ ll.assert(is_int(endIdx), "endIdx must be an integer")
344
+
345
+ exprDetails := {type: "substring", value: expression, start: start, end: endIdx}
346
+ return _newExpression(exprDetails, undefined)
347
+ },
348
+
349
+
350
+
351
+
352
+
353
+
354
+
355
+
356
+
357
+
358
+ strReplace: func(pattern, replacement, ...options) {
359
+ replaceAllVal := false
360
+ literalVal := false
361
+
362
+ if len(options) > 0 {
363
+ ll.assert(len(options) == 1, "strReplace accepts at most one options map argument.")
364
+ opsMap := options[0]
365
+ ll.assert(is_map(opsMap), "Third argument to strReplace(), if provided, must be an options map.")
366
+ if !is_undefined(opsMap.replaceAll) {
367
+ ll.assert(is_bool(opsMap.replaceAll), "replaceAll in options map must be a boolean")
368
+ replaceAllVal = opsMap.replaceAll
369
+ }
370
+ if !is_undefined(opsMap.literal) {
371
+ ll.assert(is_bool(opsMap.literal), "literal in options map must be a boolean")
372
+ literalVal = opsMap.literal
373
+ }
374
+ }
375
+
376
+ return _newExpression({
377
+ type: "str_replace",
378
+ value: expression,
379
+ pattern: _mapToSingleExpressionStruct(pattern, "lit"),
380
+ replacement: _mapToSingleExpressionStruct(replacement, "lit"),
381
+ replaceAll: replaceAllVal,
382
+ literal: literalVal
383
+ }, undefined)
384
+ },
385
+
386
+
387
+
388
+
389
+
390
+
391
+
392
+
393
+
394
+ strDistance: func(string2, metric, returnSimilarity) {
395
+ ll.assert(_isExpression(string2), "string2 must be an expression object")
396
+ ll.assert(is_string(metric), "metric must be a string")
397
+
398
+ returnSimilarityVal := false
399
+ if returnSimilarity != undefined {
400
+ ll.assert(is_bool(returnSimilarity), "returnSimilarity must be a boolean")
401
+ returnSimilarityVal = returnSimilarity
402
+ }
403
+
404
+ return _newExpression({
405
+ type: "string_distance",
406
+ string1: expression,
407
+ string2: string2.getExpression(),
408
+ metric: metric,
409
+ returnSimilarity: returnSimilarityVal
410
+ }, undefined)
411
+ },
412
+
413
+
414
+
415
+
416
+
417
+
418
+
419
+
420
+
421
+ fuzzyFilter: func(patternExpr, metric, bound) {
422
+ ll.assert(_isExpression(patternExpr), "patternExpr must be an expression object")
423
+ ll.assert(is_string(metric), "metric must be a string")
424
+ ll.assert(is_int(bound) || is_float(bound), "bound must be a number")
425
+
426
+ return _newExpression({
427
+ type: "fuzzy_string_filter",
428
+ value: expression,
429
+ pattern: patternExpr.getExpression(),
430
+ metric: metric,
431
+ bound: bound
432
+ }, undefined)
433
+ },
434
+
435
+
436
+
437
+
438
+
439
+
440
+
441
+
442
+
443
+ hash: func(hashType, encoding, bits) {
444
+ ll.assert(is_string(hashType), "hashType must be a string")
445
+ ll.assert(is_string(encoding), "encoding must be a string")
446
+ if bits != undefined {
447
+ ll.assert(is_int(bits), "bits must be a number")
448
+ }
449
+
450
+ hashExpr := {
451
+ type: "hash",
452
+ value: expression,
453
+ hashType: hashType,
454
+ encoding: encoding
455
+ }
456
+ if bits != undefined {
457
+ hashExpr.bits = bits
458
+ }
459
+ return _newExpression(hashExpr, undefined)
460
+ },
461
+
462
+
463
+
464
+
465
+
466
+
467
+
468
+ fillNull: func(fillValueExpr) {
469
+ return _newExpression(
470
+ {type: "fill_na", input: expression, fillValue: _mapToSingleExpressionStruct(fillValueExpr, "lit")},
471
+ undefined
472
+ )
473
+ },
474
+
475
+
476
+
477
+ sum: func() { return aggOp("sum") },
478
+
479
+ mean: func() { return aggOp("mean") },
480
+
481
+ median: func() { return aggOp("median") },
482
+
483
+ min: func() { return aggOp("min") },
484
+
485
+ max: func() { return aggOp("max") },
486
+
487
+ std: func() { return aggOp("std") },
488
+
489
+ var: func() { return aggOp("var") },
490
+
491
+ count: func() { return aggOp("count") },
492
+
493
+ first: func() { return aggOp("first") },
494
+
495
+ last: func() { return aggOp("last") },
496
+
497
+ nUnique: func() { return aggOp("n_unique") },
498
+
499
+
500
+ maxBy: func(...by) { return aggOpBy("max_by", by) },
501
+
502
+ minBy: func(...by) { return aggOpBy("min_by", by) },
503
+
504
+
505
+
506
+
507
+
508
+
509
+
510
+ rank: func(...descendingArgs) {
511
+ ll.assert(len(descendingArgs) <= 1, "rank method expects at most one optional boolean argument for descending.")
512
+ descendingVal := false
513
+ if len(descendingArgs) == 1 {
514
+ ll.assert(is_bool(descendingArgs[0]), "descending argument for rank must be a boolean")
515
+ descendingVal = descendingArgs[0]
516
+ }
517
+ return _newWindowExpression({
518
+ type: "rank",
519
+ orderBy: [expression],
520
+ partitionBy: [], // To be set by .over()
521
+ descending: descendingVal
522
+ }, undefined)
523
+ },
524
+
525
+
526
+
527
+
528
+
529
+
530
+
531
+
532
+
533
+
534
+
535
+
536
+ cumsum: func(...args) {
537
+ additionalOrderByVal := []
538
+ descendingVal := false
539
+ actualArgs := args
540
+
541
+ if len(args) > 0 && is_map(args[len(args)-1]) && !(_isExpression(args[len(args)-1])) {
542
+ ops := args[len(args)-1]
543
+ actualArgs = args[0:len(args)-1]
544
+ if maps.containsKey(ops, "orderBy") {
545
+ additionalOrderByVal = _mapToExpressionStructList(ops.orderBy, "col")
546
+ }
547
+ if maps.containsKey(ops, "descending") {
548
+ ll.assert(is_bool(ops.descending), "descending in options map must be a boolean")
549
+ descendingVal = ops.descending
550
+ }
551
+ }
552
+
553
+
554
+ if len(additionalOrderByVal) == 0 && len(actualArgs) > 0 {
555
+ if is_array(actualArgs[0]) || is_string(actualArgs[0]) || _isExpression(actualArgs[0]) {
556
+ additionalOrderByVal = _mapToExpressionStructList(actualArgs[0], "col")
557
+ if len(actualArgs) > 1 && is_bool(actualArgs[1]) {
558
+ descendingVal = actualArgs[1]
559
+ }
560
+ } else if is_bool(actualArgs[0]) && len(additionalOrderByVal) == 0 { // only if not set by ops.orderBy
561
+ descendingVal = actualArgs[0]
562
+ } else if len(actualArgs) > 0 {
563
+ ll.panic("Invalid arguments to cumsum. Expected order expressions or options map.")
564
+ }
565
+ }
566
+
567
+ return _newWindowExpression({
568
+ type: "cumsum",
569
+ value: expression,
570
+ additionalOrderBy: additionalOrderByVal,
571
+ partitionBy: [],
572
+ descending: descendingVal
573
+ }, undefined, undefined)
574
+ }
575
+ })
576
+ return self
577
+ }
578
+
579
+
580
+
581
+
582
+
583
+
584
+
585
+
586
+
587
+
588
+ col = func(name) {
589
+ ll.assert(is_string(name), "col name must be a string")
590
+ return _newExpression({
591
+ type: "col",
592
+ name: name
593
+ }, name)
594
+ }
595
+
596
+
597
+
598
+
599
+
600
+
601
+ lit = func(value) {
602
+ return _newExpression({
603
+ type: "const",
604
+ value: value
605
+ }, undefined)
606
+ }
607
+
608
+
609
+
610
+
611
+
612
+
613
+
614
+
615
+ concatStr := func(expressions, ...options) {
616
+ ll.assert(is_array(expressions), "First argument to concatStr must be an array of expressions")
617
+ ll.assert(len(expressions) > 0, "Expression array for concatStr cannot be empty")
618
+
619
+ delimiterVal := "" // Default to empty string
620
+ if len(options) > 0 {
621
+ ll.assert(len(options) == 1, "concatStr accepts at most one options map argument.")
622
+ opsMap := options[0]
623
+ ll.assert(is_map(opsMap), "Second argument to concatStr(), if provided, must be an options map.")
624
+ if !is_undefined(opsMap.delimiter) {
625
+ ll.assert(is_string(opsMap.delimiter), "delimiter in options map must be a string")
626
+ delimiterVal = opsMap.delimiter
627
+ }
628
+ }
629
+
630
+ return _newExpression({
631
+ type: "str_join",
632
+ operands: _mapToExpressionStructList(expressions, "lit"),
633
+ delimiter: delimiterVal
634
+ }, undefined)
635
+ }
636
+
637
+
638
+
639
+
640
+
641
+
642
+ minHorizontal := func(expressions) {
643
+ ll.assert(is_array(expressions), "Argument to minHorizontal must be an array of expressions")
644
+ ll.assert(len(expressions) > 0, "Expression array for minHorizontal cannot be empty")
645
+ return _newExpression({
646
+ type: "min",
647
+ operands: _mapToExpressionStructList(expressions, "col")
648
+ }, undefined)
649
+ }
650
+
651
+
652
+
653
+
654
+
655
+
656
+ maxHorizontal := func(expressions) {
657
+ ll.assert(is_array(expressions), "Argument to maxHorizontal must be an array of expressions")
658
+ ll.assert(len(expressions) > 0, "Expression array for maxHorizontal cannot be empty")
659
+ return _newExpression({
660
+ type: "max",
661
+ operands: _mapToExpressionStructList(expressions, "col")
662
+ }, undefined)
663
+ }
664
+
665
+
666
+
667
+
668
+
669
+
670
+ allHorizontal = func(...args){
671
+ processedOperands := []
672
+ for arg in args {
673
+ exprStruct := undefined
674
+ if _isExpression(arg) {
675
+ exprStruct = arg.getExpression()
676
+ } else if is_string(arg) {
677
+ exprStruct = col(arg).getExpression()
678
+ } else {
679
+ ll.panic("Invalid argument type for allHorizontal: %T. Must be an expression object or string.", arg)
680
+ }
681
+
682
+ if exprStruct.type == "and" && is_array(exprStruct.operands) {
683
+ processedOperands = append(processedOperands, exprStruct.operands...)
684
+ } else {
685
+ processedOperands = append(processedOperands, exprStruct)
686
+ }
687
+ }
688
+ if len(processedOperands) == 0 {
689
+ ll.panic("allHorizontal requires at least one expression.")
690
+ }
691
+ return _newExpression({
692
+ type: "and",
693
+ operands: processedOperands
694
+ }, undefined)
695
+ }
696
+
697
+
698
+
699
+
700
+
701
+
702
+ anyHorizontal = func(...args){
703
+ processedOperands := []
704
+ for arg in args {
705
+ exprStruct := undefined
706
+ if _isExpression(arg) {
707
+ exprStruct = arg.getExpression()
708
+ } else if is_string(arg) {
709
+ exprStruct = col(arg).getExpression()
710
+ } else {
711
+ ll.panic("Invalid argument type for anyHorizontal: %T. Must be an expression object or string.", arg)
712
+ }
713
+
714
+ if exprStruct.type == "or" && is_array(exprStruct.operands) {
715
+ processedOperands = append(processedOperands, exprStruct.operands...)
716
+ } else {
717
+ processedOperands = append(processedOperands, exprStruct)
718
+ }
719
+ }
720
+ if len(processedOperands) == 0 {
721
+ ll.panic("anyHorizontal requires at least one expression.")
722
+ }
723
+ return _newExpression({
724
+ type: "or",
725
+ operands: processedOperands
726
+ }, undefined)
727
+ }
728
+
729
+
730
+
731
+
732
+
733
+
734
+ and = func(...args) {
735
+ return allHorizontal(args...)
736
+ }
737
+
738
+
739
+
740
+
741
+
742
+
743
+ or = func(...args) {
744
+ return anyHorizontal(args...)
745
+ }
746
+
747
+
748
+
749
+
750
+
751
+
752
+
753
+
754
+
755
+
756
+
757
+
758
+
759
+
760
+
761
+
762
+
763
+
764
+
765
+
766
+ rank = func(orderByExpressions, ...options) {
767
+ orderByVal := []
768
+ descendingVal := false
769
+
770
+ ll.assert(orderByExpressions != undefined, "rank() requires orderByExpressions (single expression/string or array of them).")
771
+ orderByVal = _mapToExpressionStructList(orderByExpressions, "col")
772
+ ll.assert(len(orderByVal) > 0, "rank() 'orderBy' list cannot be empty after processing arguments.")
773
+
774
+ if len(options) > 0 {
775
+ ll.assert(len(options) == 1, "rank() accepts at most one options map argument.")
776
+ opsMap := options[0]
777
+ ll.assert(is_map(opsMap), "Second argument to rank(), if provided, must be an options map.")
778
+ if maps.containsKey(opsMap, "descending") {
779
+ ll.assert(is_bool(opsMap.descending), "descending in options map must be a boolean")
780
+ descendingVal = opsMap.descending
781
+ }
782
+ }
783
+
784
+ return _newWindowExpression({
785
+ type: "rank",
786
+ orderBy: orderByVal,
787
+ partitionBy: [],
788
+ descending: descendingVal
789
+ }, undefined, undefined)
790
+ }
791
+
792
+
793
+ _newWhenThenBuilder := func(currentClauses, currentWhenExprOrUndefined) {
794
+ builderSelf := undefined
795
+ builderSelf = ll.toStrict({
796
+
797
+
798
+
799
+
800
+
801
+ then: func(thenExpr) {
802
+ ll.assert(currentWhenExprOrUndefined != undefined, ".then() must follow a .when() or initial when() call.")
803
+ ll.assert(_isExpression(thenExpr), ".then() expects an expression object argument.")
804
+
805
+
806
+ newClauses := maps.clone(currentClauses) // Deep copy existing clauses
807
+ newClauses = append(newClauses, {
808
+ when: currentWhenExprOrUndefined.getExpression(),
809
+ then: thenExpr.getExpression()
810
+ })
811
+
812
+
813
+ return _newWhenThenBuilder(newClauses, undefined)
814
+ },
815
+
816
+
817
+
818
+
819
+
820
+
821
+ when: func(conditionExpr) {
822
+ ll.assert(currentWhenExprOrUndefined == undefined, ".when() must follow a .then() call.")
823
+ ll.assert(_isExpression(conditionExpr), ".when() expects an expression object argument.")
824
+
825
+
826
+
827
+ return _newWhenThenBuilder(currentClauses, conditionExpr)
828
+ },
829
+
830
+
831
+
832
+
833
+
834
+
835
+ otherwise: func(otherwiseExpr) {
836
+ ll.assert(currentWhenExprOrUndefined == undefined, ".otherwise() must follow a .then() call.")
837
+ ll.assert(len(currentClauses) > 0, "At least one .when().then() clause is required before .otherwise().")
838
+ ll.assert(_isExpression(otherwiseExpr), ".otherwise() expects an expression object argument.")
839
+
840
+
841
+ finalClauses := maps.clone(currentClauses)
842
+ return _newExpression({
843
+ type: "when_then_otherwise",
844
+ conditions: finalClauses,
845
+ otherwise: otherwiseExpr.getExpression()
846
+ }, undefined)
847
+ }
848
+ })
849
+ return builderSelf
850
+ }
851
+
852
+
853
+
854
+
855
+
856
+
857
+
858
+
859
+
860
+
861
+
862
+ when = func(conditionExpr) {
863
+ ll.assert(_isExpression(conditionExpr), "Initial when() expects an expression object argument.")
864
+ return _newWhenThenBuilder([], conditionExpr)
865
+ }
866
+
867
+
868
+
869
+
870
+
871
+
872
+
873
+
874
+
875
+
876
+
877
+
878
+ rawExp := func(expression, ...alias) {
879
+ ll.assert(len(alias) <= 1, "rawExpression expects at most one alias argument")
880
+ aliasVal := undefined
881
+ if len(alias) > 0 {
882
+ aliasVal = alias[0]
883
+ }
884
+ ll.assert(is_map(expression), "rawExpression expects a map as argument")
885
+ return _newExpression(expression, aliasVal)
886
+ }
887
+
888
+ export ll.toStrict({
889
+ col: col,
890
+ lit: lit,
891
+ concatStr: concatStr,
892
+ minHorizontal: minHorizontal,
893
+ maxHorizontal: maxHorizontal,
894
+ allHorizontal: allHorizontal,
895
+ anyHorizontal: anyHorizontal,
896
+ and: and, // Top-level alias for allHorizontal
897
+ or: or, // Top-level alias for anyHorizontal
898
+ rank: rank,
899
+ when: when,
900
+ rawExp: rawExp,
901
+ _isExpression: _isExpression,
902
+ _isAggregation: _isAggregation,
903
+ _mapToExpressionStructList: _mapToExpressionStructList,
904
+ _mapToSingleExpressionStruct: _mapToSingleExpressionStruct
905
+ })