@chrismo/superkit 1.0.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.
Files changed (74) hide show
  1. package/LICENSE.txt +29 -0
  2. package/README.md +26 -0
  3. package/dist/cli/pager.d.ts +6 -0
  4. package/dist/cli/pager.d.ts.map +1 -0
  5. package/dist/cli/pager.js +21 -0
  6. package/dist/cli/pager.js.map +1 -0
  7. package/dist/cli/skdoc.d.ts +3 -0
  8. package/dist/cli/skdoc.d.ts.map +1 -0
  9. package/dist/cli/skdoc.js +42 -0
  10. package/dist/cli/skdoc.js.map +1 -0
  11. package/dist/cli/skgrok.d.ts +3 -0
  12. package/dist/cli/skgrok.d.ts.map +1 -0
  13. package/dist/cli/skgrok.js +21 -0
  14. package/dist/cli/skgrok.js.map +1 -0
  15. package/dist/cli/skops.d.ts +3 -0
  16. package/dist/cli/skops.d.ts.map +1 -0
  17. package/dist/cli/skops.js +32 -0
  18. package/dist/cli/skops.js.map +1 -0
  19. package/dist/index.d.ts +10 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +11 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/lib/docs.d.ts +11 -0
  24. package/dist/lib/docs.d.ts.map +1 -0
  25. package/dist/lib/docs.js +29 -0
  26. package/dist/lib/docs.js.map +1 -0
  27. package/dist/lib/expert-sections.d.ts +32 -0
  28. package/dist/lib/expert-sections.d.ts.map +1 -0
  29. package/dist/lib/expert-sections.js +130 -0
  30. package/dist/lib/expert-sections.js.map +1 -0
  31. package/dist/lib/grok.d.ts +15 -0
  32. package/dist/lib/grok.d.ts.map +1 -0
  33. package/dist/lib/grok.js +57 -0
  34. package/dist/lib/grok.js.map +1 -0
  35. package/dist/lib/help.d.ts +20 -0
  36. package/dist/lib/help.d.ts.map +1 -0
  37. package/dist/lib/help.js +163 -0
  38. package/dist/lib/help.js.map +1 -0
  39. package/dist/lib/recipes.d.ts +29 -0
  40. package/dist/lib/recipes.d.ts.map +1 -0
  41. package/dist/lib/recipes.js +133 -0
  42. package/dist/lib/recipes.js.map +1 -0
  43. package/dist/superkit.tar.gz +0 -0
  44. package/docs/grok-patterns.sup +89 -0
  45. package/docs/recipes/array.md +66 -0
  46. package/docs/recipes/array.spq +31 -0
  47. package/docs/recipes/character.md +110 -0
  48. package/docs/recipes/character.spq +57 -0
  49. package/docs/recipes/escape.md +159 -0
  50. package/docs/recipes/escape.spq +102 -0
  51. package/docs/recipes/format.md +51 -0
  52. package/docs/recipes/format.spq +24 -0
  53. package/docs/recipes/index.md +23 -0
  54. package/docs/recipes/integer.md +101 -0
  55. package/docs/recipes/integer.spq +53 -0
  56. package/docs/recipes/records.md +84 -0
  57. package/docs/recipes/records.spq +61 -0
  58. package/docs/recipes/string.md +177 -0
  59. package/docs/recipes/string.spq +105 -0
  60. package/docs/superdb-expert.md +929 -0
  61. package/docs/tutorials/bash_to_sup.md +123 -0
  62. package/docs/tutorials/chess-tiebreaks.md +233 -0
  63. package/docs/tutorials/debug.md +439 -0
  64. package/docs/tutorials/fork_for_window.md +296 -0
  65. package/docs/tutorials/grok.md +166 -0
  66. package/docs/tutorials/index.md +10 -0
  67. package/docs/tutorials/joins.md +79 -0
  68. package/docs/tutorials/moar_subqueries.md +35 -0
  69. package/docs/tutorials/subqueries.md +236 -0
  70. package/docs/tutorials/sup_to_bash.md +164 -0
  71. package/docs/tutorials/super_db_update.md +34 -0
  72. package/docs/tutorials/unnest.md +113 -0
  73. package/docs/zq-to-super-upgrades.md +549 -0
  74. package/package.json +46 -0
@@ -0,0 +1,57 @@
1
+ -- sk_include string.spq
2
+
3
+ op skdoc_seq: (
4
+ cast(
5
+ {name:"sk_seq",
6
+ type:"op",
7
+ desc:"Generates a sequence 0..n-1 (like generate_series).",
8
+ args:[{name:"n",desc:"Number of elements to generate"}],
9
+ examples:[{i:"sk_seq(3)",o:"0, 1, 2"}] }, <skdoc>)
10
+ )
11
+
12
+ op sk_seq(n): (
13
+ split(sk_pad_left('', '0', n), '')
14
+ | unnest this
15
+ | count
16
+ | values count - 1
17
+ )
18
+
19
+ fn skdoc_hex_digits(): (
20
+ cast(
21
+ {name:"sk_hex_digits",
22
+ type:"func",
23
+ desc:"Returns the 16 hex digit characters as an array.",
24
+ args:[],
25
+ examples:[] }, <skdoc>)
26
+ )
27
+
28
+ fn sk_hex_digits(): (
29
+ split("0123456789abcdef", "")
30
+ )
31
+
32
+ fn skdoc_chr(): (
33
+ cast(
34
+ {name:"sk_chr",
35
+ type:"func",
36
+ desc:"Converts an integer (0-127) to its ASCII character.",
37
+ args:[{name:"code",desc:"ASCII code (0-127)"}],
38
+ examples:[{i:"sk_chr(65)",o:"'A'"}] }, <skdoc>)
39
+ )
40
+
41
+ fn sk_chr(code): (
42
+ let d = sk_hex_digits() |
43
+ hex(f'{d[code/16]}{d[code%16]}')::string
44
+ )
45
+
46
+ fn skdoc_alpha(): (
47
+ cast(
48
+ {name:"sk_alpha",
49
+ type:"func",
50
+ desc:"Converts 1-26 to A-Z.",
51
+ args:[{name:"n",desc:"Number 1-26"}],
52
+ examples:[{i:"sk_alpha(3)",o:"'C'"}] }, <skdoc>)
53
+ )
54
+
55
+ fn sk_alpha(n): (
56
+ sk_chr(64 + n)
57
+ )
@@ -0,0 +1,159 @@
1
+ ---
2
+ title: Escape
3
+ layout: default
4
+ nav_order: 7
5
+ parent: Recipes
6
+ ---
7
+
8
+ # Escape Recipes
9
+
10
+ Source: `escape.spq`
11
+
12
+ Functions for safely escaping values for CSV, TSV, and shell contexts, plus
13
+ patterns for safe text ingestion from shell pipelines.
14
+
15
+ ---
16
+
17
+ ## sk_csv_field
18
+
19
+ Escapes a string for use as a CSV field per RFC 4180. Wraps in double quotes
20
+ and doubles internal quotes when the value contains commas, quotes, or
21
+ newlines. Plain values pass through unchanged.
22
+
23
+ **Type:** function
24
+
25
+ | Argument | Description |
26
+ |----------|-------------|
27
+ | `s` | The string to escape |
28
+
29
+ ```supersql
30
+ sk_csv_field('plain')
31
+ -- => 'plain'
32
+
33
+ sk_csv_field('hello, world')
34
+ -- => quoted and wrapped
35
+ ```
36
+
37
+ **Implementation:**
38
+
39
+ ```supersql
40
+ fn sk_csv_field(s): (
41
+ grep("[,\"\n]", s)
42
+ ? f"\"{replace(s, "\"", "\"\"")}\""
43
+ : s
44
+ )
45
+ ```
46
+
47
+ ---
48
+
49
+ ## sk_csv_row
50
+
51
+ Builds a CSV row from an array of values. Each element is cast to string and
52
+ escaped with sk_csv_field, then joined with commas.
53
+
54
+ **Type:** function
55
+
56
+ | Argument | Description |
57
+ |----------|-------------|
58
+ | `arr` | Array of values to format as a CSV row |
59
+
60
+ **Implementation:**
61
+
62
+ ```supersql
63
+ fn sk_csv_row(arr): (
64
+ join([unnest arr | values sk_csv_field(cast(this, <string>))], ",")
65
+ )
66
+ ```
67
+
68
+ ---
69
+
70
+ ## sk_shell_quote
71
+
72
+ Wraps a string in POSIX shell single quotes. Internal single quotes are escaped
73
+ so the result is safe for shell interpolation. Protects against injection of `$`,
74
+ backticks, and other shell metacharacters.
75
+
76
+ **Type:** function
77
+
78
+ | Argument | Description |
79
+ |----------|-------------|
80
+ | `s` | The string to quote |
81
+
82
+ ```supersql
83
+ sk_shell_quote('hello world')
84
+ -- => single-quoted string
85
+
86
+ sk_shell_quote('has $var')
87
+ -- => single-quoted, $ not expanded
88
+ ```
89
+
90
+ **Implementation:**
91
+
92
+ ```supersql
93
+ fn sk_shell_quote(s): (
94
+ f"'{replace(s, "'", "'\\''")}'"
95
+ )
96
+ ```
97
+
98
+ ---
99
+
100
+ ## sk_tsv_field
101
+
102
+ Escapes a value for use in a TSV field. Casts to string, then replaces literal
103
+ tab and newline characters with their backslash-escaped forms.
104
+
105
+ **Type:** function
106
+
107
+ | Argument | Description |
108
+ |----------|-------------|
109
+ | `s` | The value to escape |
110
+
111
+ **Implementation:**
112
+
113
+ ```supersql
114
+ fn sk_tsv_field(s): (
115
+ replace(replace(cast(s, <string>), "\t", "\\t"), "\n", "\\n")
116
+ )
117
+ ```
118
+
119
+ ---
120
+
121
+ ## Shell Patterns for Safe Text Ingestion
122
+
123
+ The key insight: never interpolate untrusted text into a SuperQL string literal.
124
+ Pipe raw text through `super` with `-i line` and let the serializer handle
125
+ escaping. These patterns work from any language that can spawn a subprocess.
126
+
127
+ ### safe_text_to_record
128
+
129
+ Pipe raw text into super to build a record without string interpolation.
130
+
131
+ ```bash
132
+ echo "$text" | super -s -i line -c "values {body: this}" -
133
+ ```
134
+
135
+ ### safe_text_to_string
136
+
137
+ Pipe raw text through super to get a properly escaped SUP string literal.
138
+
139
+ ```bash
140
+ echo "$text" | super -s -i line -c "values this" -
141
+ ```
142
+
143
+ ### safe_multiline_to_record
144
+
145
+ Collapse multiline text into a single record field.
146
+
147
+ ```bash
148
+ echo "$text" | super -s -i line \
149
+ -c 'aggregate s:=collect(this) | values {body: join(s, "\n")}' -
150
+ ```
151
+
152
+ ### safe_append_to_sup_file
153
+
154
+ Append a timestamped record with raw text to a `.sup` file.
155
+
156
+ ```bash
157
+ echo "$text" | super -s -i line \
158
+ -c "values {ts: now(), body: this}" - >> data.sup
159
+ ```
@@ -0,0 +1,102 @@
1
+ fn skdoc_csv_field(): (
2
+ cast(
3
+ {name:"sk_csv_field",
4
+ type:"func",
5
+ desc:"Escapes a string for use as a CSV field per RFC 4180. Wraps in double quotes and doubles internal quotes when the value contains commas, quotes, or newlines. Plain values pass through unchanged.",
6
+ args:[{name:"s",desc:"The string to escape"}],
7
+ examples:[{i:"sk_csv_field('plain')",o:"'plain'"}
8
+ {i:"sk_csv_field('hello, world')",o:"quoted and wrapped"}
9
+ {i:"sk_csv_field('has newline')",o:"quoted and wrapped"}] }, <skdoc>)
10
+ )
11
+
12
+ fn sk_csv_field(s): (
13
+ grep("[,\"\n]", s)
14
+ ? f"\"{replace(s, "\"", "\"\"")}\""
15
+ : s
16
+ )
17
+
18
+ fn skdoc_csv_row(): (
19
+ cast(
20
+ {name:"sk_csv_row",
21
+ type:"func",
22
+ desc:"Builds a CSV row from an array of values. Each element is cast to string and escaped with sk_csv_field, then joined with commas.",
23
+ args:[{name:"arr",desc:"Array of values to format as a CSV row"}],
24
+ examples:[{i:"sk_csv_row(arr) where arr has commas",o:"fields with commas get quoted"}] }, <skdoc>)
25
+ )
26
+
27
+ fn sk_csv_row(arr): (
28
+ join([unnest arr | values sk_csv_field(cast(this, <string>))], ",")
29
+ )
30
+
31
+ fn skdoc_shell_quote(): (
32
+ cast(
33
+ {name:"sk_shell_quote",
34
+ type:"func",
35
+ desc:"Wraps a string in POSIX shell single quotes. Internal single quotes are escaped so the result is safe for shell interpolation. Protects against injection of $, backticks, and other shell metacharacters.",
36
+ args:[{name:"s",desc:"The string to quote"}],
37
+ examples:[{i:"sk_shell_quote('hello world')",o:"single-quoted string"}
38
+ {i:"sk_shell_quote('has $var')",o:"single-quoted, $ not expanded"}] }, <skdoc>)
39
+ )
40
+
41
+ fn sk_shell_quote(s): (
42
+ f"'{replace(s, "'", "'\\'')}'"
43
+ )
44
+
45
+ fn skdoc_tsv_field(): (
46
+ cast(
47
+ {name:"sk_tsv_field",
48
+ type:"func",
49
+ desc:"Escapes a value for use in a TSV field. Casts to string, then replaces literal tab and newline characters with their backslash-escaped forms.",
50
+ args:[{name:"s",desc:"The value to escape"}],
51
+ examples:[{i:"sk_tsv_field('col1\\tcol2')",o:"tabs replaced with literal \\t"}
52
+ {i:"sk_tsv_field('line1\\nline2')",o:"newlines replaced with literal \\n"}] }, <skdoc>)
53
+ )
54
+
55
+ fn sk_tsv_field(s): (
56
+ replace(replace(cast(s, <string>), "\t", "\\t"), "\n", "\\n")
57
+ )
58
+
59
+ -- Shell patterns for safe text ingestion
60
+ -- The key insight: never interpolate untrusted text into a SuperQL string literal.
61
+ -- Pipe raw text through super with -i line and let the serializer handle escaping.
62
+ -- These patterns work from any language that can spawn a subprocess.
63
+
64
+ fn skdoc_safe_text_to_record(): (
65
+ cast(
66
+ {name:"safe_text_to_record",
67
+ type:"shell",
68
+ desc:"Pipe raw text into super to build a record without string interpolation. The text never passes through shell expansion or manual escaping. Works from any language via subprocess.",
69
+ args:[],
70
+ snippet:'echo "$text" | super -s -i line -c "values {body: this}" -',
71
+ examples:[{i:"text with quotes and $pecial chars",o:"safely embedded in a .sup record"}] }, <skdoc>)
72
+ )
73
+
74
+ fn skdoc_safe_text_to_string(): (
75
+ cast(
76
+ {name:"safe_text_to_string",
77
+ type:"shell",
78
+ desc:"Pipe raw text through super to get a properly escaped SUP string literal. Useful when you need the escaped value for embedding in other SUP data.",
79
+ args:[],
80
+ snippet:'echo "$text" | super -s -i line -c "values this" -',
81
+ examples:[{i:"She said hello and backslash",o:"properly escaped SUP string"}] }, <skdoc>)
82
+ )
83
+
84
+ fn skdoc_safe_multiline_to_record(): (
85
+ cast(
86
+ {name:"safe_multiline_to_record",
87
+ type:"shell",
88
+ desc:"Collapse multiline text into a single record field. Each line is collected then joined with newline characters. The text is never interpolated through the shell.",
89
+ args:[],
90
+ snippet:'echo "$text" | super -s -i line -c "aggregate s:=collect(this) | values {body: join(s, \"\\n\")}" -',
91
+ examples:[{i:"multi-line text with special chars",o:"single record with embedded newlines"}] }, <skdoc>)
92
+ )
93
+
94
+ fn skdoc_safe_append_to_sup_file(): (
95
+ cast(
96
+ {name:"safe_append_to_sup_file",
97
+ type:"shell",
98
+ desc:"Append a timestamped record with raw text to a .sup file. Common pattern for scripts that log or accumulate structured data from unstructured input.",
99
+ args:[],
100
+ snippet:'echo "$text" | super -s -i line -c "values {ts: now(), body: this}" - >> data.sup',
101
+ examples:[{i:"arbitrary user input",o:"appended as timestamped record to .sup file"}] }, <skdoc>)
102
+ )
@@ -0,0 +1,51 @@
1
+ ---
2
+ title: Format
3
+ layout: default
4
+ nav_order: 5
5
+ parent: Recipes
6
+ ---
7
+
8
+ # Format Recipes
9
+
10
+ Source: `format.spq`
11
+
12
+ ---
13
+
14
+ ## sk_format_bytes
15
+
16
+ Returns the size in bytes in human readable format.
17
+
18
+ **Type:** function
19
+
20
+ | Argument | Description |
21
+ |----------|-------------|
22
+ | `value` | Must be castable to uint64 |
23
+
24
+ ```supersql
25
+ sk_format_bytes(1048576)
26
+ -- => '1 MB'
27
+
28
+ sk_format_bytes(0)
29
+ -- => '0 B'
30
+ ```
31
+
32
+ Supports units up to EB (exabytes). The full unit list: B, KB, MB, GB, TB, PB, EB.
33
+
34
+ **Implementation:**
35
+
36
+ ```supersql
37
+ const sk_bytes_units=["B", "KB", "MB", "GB", "TB", "PB", "EB"]
38
+ const sk_bytes_divisor=1024
39
+
40
+ fn _sk_bytes_unit_index(value): (
41
+ floor(log(value) / log(sk_bytes_divisor))::uint64
42
+ )
43
+
44
+ fn _sk_format_nonzero_bytes(value): (
45
+ f"{(value / pow(sk_bytes_divisor, _sk_bytes_unit_index(value)))::uint64} {sk_bytes_units[_sk_bytes_unit_index(value)]}"
46
+ )
47
+
48
+ fn sk_format_bytes(value): (
49
+ (value == 0) ? "0 B" : _sk_format_nonzero_bytes(value)
50
+ )
51
+ ```
@@ -0,0 +1,24 @@
1
+ -- cannot get above EB with uint64 - sorry "ZB" & "YB" and beyond...
2
+ const sk_bytes_units=["B", "KB", "MB", "GB", "TB", "PB", "EB"]
3
+ const sk_bytes_divisor=1024
4
+
5
+ fn _sk_bytes_unit_index(value): (
6
+ floor(log(value) / log(sk_bytes_divisor))::uint64
7
+ )
8
+
9
+ fn _sk_format_nonzero_bytes(value): (
10
+ f"{(value / pow(sk_bytes_divisor, _sk_bytes_unit_index(value)))::uint64} {sk_bytes_units[_sk_bytes_unit_index(value)]}"
11
+ )
12
+
13
+ fn skdoc_format_bytes(): (
14
+ cast(
15
+ {name:"sk_format_bytes",
16
+ type:"func",
17
+ desc:"Returns the size in bytes in human readable format.",
18
+ args:[{name:"value",desc:"Must be castable to uint64"}],
19
+ examples:[{i:"sk_format_bytes(1048576)",o:"'1 MB'"}] }, <skdoc>)
20
+ )
21
+
22
+ fn sk_format_bytes(value): (
23
+ (value == 0) ? "0 B" : _sk_format_nonzero_bytes(value)
24
+ )
@@ -0,0 +1,23 @@
1
+ ---
2
+ title: Recipes
3
+ layout: default
4
+ has_children: true
5
+ nav_order: 5
6
+ ---
7
+
8
+ # Recipes
9
+
10
+ Reusable SuperSQL functions and operators for common tasks. Include them in your
11
+ queries with `from` or `-I`:
12
+
13
+ ```supersql
14
+ from 'string.spq' | values sk_capitalize('hello')
15
+ ```
16
+
17
+ ```bash
18
+ super -I string.spq -c "values sk_capitalize('hello')"
19
+ ```
20
+
21
+ Recipe source files are available in the
22
+ [superkit](https://github.com/chrismo/superkit/tree/main/docs/recipes)
23
+ repository.
@@ -0,0 +1,101 @@
1
+ ---
2
+ title: Integer
3
+ layout: default
4
+ nav_order: 3
5
+ parent: Recipes
6
+ ---
7
+
8
+ # Integer Recipes
9
+
10
+ Source: `integer.spq`
11
+
12
+ ---
13
+
14
+ ## sk_clamp
15
+
16
+ Clamps a value between min and max.
17
+
18
+ **Type:** function
19
+
20
+ | Argument | Description |
21
+ |----------|-------------|
22
+ | `i` | The value to clamp. |
23
+ | `min` | The minimum value. |
24
+ | `max` | The maximum value. |
25
+
26
+ ```supersql
27
+ sk_clamp(1, 2, 3)
28
+ -- => 2
29
+
30
+ sk_clamp(4, 2, 3)
31
+ -- => 3
32
+
33
+ sk_clamp(2, 2, 3)
34
+ -- => 2
35
+ ```
36
+
37
+ **Implementation:**
38
+
39
+ ```supersql
40
+ fn sk_clamp(i, min, max): (
41
+ i < min ? min : i > max ? max : i
42
+ )
43
+ ```
44
+
45
+ ---
46
+
47
+ ## sk_min
48
+
49
+ Returns the minimum of two values.
50
+
51
+ **Type:** function
52
+
53
+ | Argument | Description |
54
+ |----------|-------------|
55
+ | `a` | The first value. |
56
+ | `b` | The second value. |
57
+
58
+ ```supersql
59
+ sk_min(1, 2)
60
+ -- => 1
61
+
62
+ sk_min(3, 2)
63
+ -- => 2
64
+ ```
65
+
66
+ **Implementation:**
67
+
68
+ ```supersql
69
+ fn sk_min(a, b): (
70
+ a < b ? a : b
71
+ )
72
+ ```
73
+
74
+ ---
75
+
76
+ ## sk_max
77
+
78
+ Returns the maximum of two values.
79
+
80
+ **Type:** function
81
+
82
+ | Argument | Description |
83
+ |----------|-------------|
84
+ | `a` | The first value. |
85
+ | `b` | The second value. |
86
+
87
+ ```supersql
88
+ sk_max(1, 2)
89
+ -- => 2
90
+
91
+ sk_max(3, 2)
92
+ -- => 3
93
+ ```
94
+
95
+ **Implementation:**
96
+
97
+ ```supersql
98
+ fn sk_max(a, b): (
99
+ a > b ? a : b
100
+ )
101
+ ```
@@ -0,0 +1,53 @@
1
+ -- TODO: this doesn't cause any problems yet because we're not recursively
2
+ -- scanning sk_include files for _their_ sk_includes.
3
+
4
+ -- sk_include string.spq -- test circular dependencies
5
+
6
+ op skdoc_clamp: (
7
+ cast(
8
+ {name:"sk_clamp",
9
+ type:"func",
10
+ desc:"Clamps a value between min and max.",
11
+ args:[{name:"i",desc:"The value to clamp."}
12
+ {name:"min",desc:"The minimum value."}
13
+ {name:"max",desc:"The maximum value."}],
14
+ examples:[{i:"sk_clamp(1, 2, 3)",o:"2"}
15
+ {i:"sk_clamp(4, 2, 3)",o:"3"}
16
+ {i:"sk_clamp(2, 2, 3)",o:"2"}]}, <skdoc>)
17
+ )
18
+
19
+ fn sk_clamp(i, min, max): (
20
+ i < min ? min : i > max ? max : i
21
+ )
22
+
23
+ fn skdoc_min(): (
24
+ cast(
25
+ {name:"sk_min",
26
+ type:"func",
27
+ desc:"Returns the minimum of two values.",
28
+ args:[{name:"a",desc:"The first value."}
29
+ {name:"b",desc:"The second value."}],
30
+ examples:[{i:"sk_min(1, 2)",o:"1"}
31
+ {i:"sk_min(3, 2)",o:"2"}
32
+ {i:"sk_min(2, 2)",o:"2"}]}, <skdoc>)
33
+ )
34
+
35
+ fn sk_min(a, b): (
36
+ a < b ? a : b
37
+ )
38
+
39
+ fn skdoc_max(): (
40
+ cast(
41
+ {name:"sk_max",
42
+ type:"func",
43
+ desc:"Returns the maximum of two values.",
44
+ args:[{name:"a",desc:"The first value."}
45
+ {name:"b",desc:"The second value."}],
46
+ examples:[{i:"sk_max(1, 2)",o:"2"}
47
+ {i:"sk_max(3, 2)",o:"3"}
48
+ {i:"sk_max(2, 2)",o:"2"}]}, <skdoc>)
49
+ )
50
+
51
+ fn sk_max(a, b): (
52
+ a > b ? a : b
53
+ )
@@ -0,0 +1,84 @@
1
+ ---
2
+ title: Records
3
+ layout: default
4
+ nav_order: 6
5
+ parent: Recipes
6
+ ---
7
+
8
+ # Record Recipes
9
+
10
+ Source: `records.spq` (includes `array.spq`)
11
+
12
+ ---
13
+
14
+ ## sk_keys
15
+
16
+ Returns the keys of the top-level fields in a record. This does not go deep
17
+ into nested records.
18
+
19
+ **Type:** operator
20
+
21
+ ```supersql
22
+ {a:1,b:{c:333}} | sk_keys
23
+ -- => ['a','b']
24
+
25
+ {x:10,y:20,z:30} | sk_keys
26
+ -- => ['x','y','z']
27
+ ```
28
+
29
+ **Implementation:**
30
+
31
+ ```supersql
32
+ op sk_keys: (
33
+ fields(this) | unnest this | values this[0] | uniq | collect(this)
34
+ )
35
+ ```
36
+
37
+ ---
38
+
39
+ ## sk_merge_records
40
+
41
+ Merges an array of records into a single record by combining the fields. If
42
+ there are duplicate keys, the last one wins.
43
+
44
+ **Type:** operator
45
+
46
+ ```supersql
47
+ [{a:1},{b:{c:333}}] | sk_merge_records
48
+ -- => {a:1,b:{c:333}}
49
+ ```
50
+
51
+ **Implementation:**
52
+
53
+ ```supersql
54
+ op sk_merge_records: (
55
+ this::string
56
+ | replace(this, "},{",",")
57
+ | parse_sup(this[1:-1])
58
+ )
59
+ ```
60
+
61
+ ---
62
+
63
+ ## sk_add_ids
64
+
65
+ Prepends an incrementing id field to each record. Always returns an array.
66
+
67
+ **Type:** operator
68
+
69
+ ```supersql
70
+ [{a:3},{b:4}] | sk_add_ids
71
+ -- => [{id:1,a:3},{id:2,b:4}]
72
+ ```
73
+
74
+ **Implementation:**
75
+
76
+ ```supersql
77
+ op sk_add_ids: (
78
+ sk_in_array(this)
79
+ | unnest this
80
+ | count
81
+ | values {id:count,...that}
82
+ | collect(this)
83
+ )
84
+ ```