rowpipe 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +327 -41
- package/dist/cli/commands/diff.d.ts +30 -0
- package/dist/cli/commands/diff.d.ts.map +1 -0
- package/dist/cli/commands/diff.js +143 -0
- package/dist/cli/commands/diff.js.map +1 -0
- package/dist/cli/commands/files.d.ts +35 -0
- package/dist/cli/commands/files.d.ts.map +1 -0
- package/dist/cli/commands/files.js +93 -0
- package/dist/cli/commands/files.js.map +1 -0
- package/dist/cli/index.js +89 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/core/errors.d.ts +23 -0
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +38 -0
- package/dist/core/errors.js.map +1 -1
- package/dist/core/types.d.ts +4 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/diff/comparator.d.ts +17 -0
- package/dist/diff/comparator.d.ts.map +1 -0
- package/dist/diff/comparator.js +158 -0
- package/dist/diff/comparator.js.map +1 -0
- package/dist/diff/engine.d.ts +10 -0
- package/dist/diff/engine.d.ts.map +1 -0
- package/dist/diff/engine.js +333 -0
- package/dist/diff/engine.js.map +1 -0
- package/dist/diff/hash.d.ts +6 -0
- package/dist/diff/hash.d.ts.map +1 -0
- package/dist/diff/hash.js +50 -0
- package/dist/diff/hash.js.map +1 -0
- package/dist/diff/key.d.ts +21 -0
- package/dist/diff/key.d.ts.map +1 -0
- package/dist/diff/key.js +107 -0
- package/dist/diff/key.js.map +1 -0
- package/dist/diff/reporter.d.ts +18 -0
- package/dist/diff/reporter.d.ts.map +1 -0
- package/dist/diff/reporter.js +133 -0
- package/dist/diff/reporter.js.map +1 -0
- package/dist/diff/schema.d.ts +7 -0
- package/dist/diff/schema.d.ts.map +1 -0
- package/dist/diff/schema.js +65 -0
- package/dist/diff/schema.js.map +1 -0
- package/dist/diff/storage/disk-index.d.ts +28 -0
- package/dist/diff/storage/disk-index.d.ts.map +1 -0
- package/dist/diff/storage/disk-index.js +105 -0
- package/dist/diff/storage/disk-index.js.map +1 -0
- package/dist/diff/storage/memory-index.d.ts +23 -0
- package/dist/diff/storage/memory-index.d.ts.map +1 -0
- package/dist/diff/storage/memory-index.js +69 -0
- package/dist/diff/storage/memory-index.js.map +1 -0
- package/dist/diff/storage/spillable-index.d.ts +30 -0
- package/dist/diff/storage/spillable-index.d.ts.map +1 -0
- package/dist/diff/storage/spillable-index.js +108 -0
- package/dist/diff/storage/spillable-index.js.map +1 -0
- package/dist/diff/types.d.ts +127 -0
- package/dist/diff/types.d.ts.map +1 -0
- package/dist/diff/types.js +2 -0
- package/dist/diff/types.js.map +1 -0
- package/dist/files/glob.d.ts +21 -0
- package/dist/files/glob.d.ts.map +1 -0
- package/dist/files/glob.js +132 -0
- package/dist/files/glob.js.map +1 -0
- package/dist/files/hash.d.ts +10 -0
- package/dist/files/hash.d.ts.map +1 -0
- package/dist/files/hash.js +67 -0
- package/dist/files/hash.js.map +1 -0
- package/dist/files/index.d.ts +6 -0
- package/dist/files/index.d.ts.map +1 -0
- package/dist/files/index.js +6 -0
- package/dist/files/index.js.map +1 -0
- package/dist/files/mime.d.ts +9 -0
- package/dist/files/mime.d.ts.map +1 -0
- package/dist/files/mime.js +128 -0
- package/dist/files/mime.js.map +1 -0
- package/dist/files/reader.d.ts +19 -0
- package/dist/files/reader.d.ts.map +1 -0
- package/dist/files/reader.js +344 -0
- package/dist/files/reader.js.map +1 -0
- package/dist/files/types.d.ts +47 -0
- package/dist/files/types.d.ts.map +1 -0
- package/dist/files/types.js +2 -0
- package/dist/files/types.js.map +1 -0
- package/dist/index.d.ts +17 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -2
- package/dist/index.js.map +1 -1
- package/dist/readers/csv.d.ts.map +1 -1
- package/dist/readers/csv.js +59 -27
- package/dist/readers/csv.js.map +1 -1
- package/dist/readers/index.d.ts +2 -0
- package/dist/readers/index.d.ts.map +1 -1
- package/dist/readers/index.js +57 -3
- package/dist/readers/index.js.map +1 -1
- package/dist/readers/json.d.ts.map +1 -1
- package/dist/readers/json.js +6 -8
- package/dist/readers/json.js.map +1 -1
- package/dist/readers/jsonl.d.ts.map +1 -1
- package/dist/readers/jsonl.js +5 -7
- package/dist/readers/jsonl.js.map +1 -1
- package/dist/readers/parquet.d.ts +17 -0
- package/dist/readers/parquet.d.ts.map +1 -0
- package/dist/readers/parquet.js +146 -0
- package/dist/readers/parquet.js.map +1 -0
- package/dist/transforms/expression.d.ts.map +1 -1
- package/dist/transforms/expression.js +73 -7
- package/dist/transforms/expression.js.map +1 -1
- package/dist/utils/compression.d.ts +30 -6
- package/dist/utils/compression.d.ts.map +1 -1
- package/dist/utils/compression.js +95 -18
- package/dist/utils/compression.js.map +1 -1
- package/dist/writers/csv.d.ts.map +1 -1
- package/dist/writers/csv.js +29 -17
- package/dist/writers/csv.js.map +1 -1
- package/dist/writers/index.d.ts +2 -0
- package/dist/writers/index.d.ts.map +1 -1
- package/dist/writers/index.js +49 -4
- package/dist/writers/index.js.map +1 -1
- package/dist/writers/json.d.ts +1 -1
- package/dist/writers/json.d.ts.map +1 -1
- package/dist/writers/json.js +9 -11
- package/dist/writers/json.js.map +1 -1
- package/dist/writers/jsonl.d.ts +1 -1
- package/dist/writers/jsonl.d.ts.map +1 -1
- package/dist/writers/jsonl.js +9 -11
- package/dist/writers/jsonl.js.map +1 -1
- package/dist/writers/markdown.d.ts +21 -0
- package/dist/writers/markdown.d.ts.map +1 -0
- package/dist/writers/markdown.js +82 -0
- package/dist/writers/markdown.js.map +1 -0
- package/dist/writers/parquet.d.ts +19 -0
- package/dist/writers/parquet.d.ts.map +1 -0
- package/dist/writers/parquet.js +129 -0
- package/dist/writers/parquet.js.map +1 -0
- package/package.json +16 -3
- package/skills/rowpipe/SKILL.md +263 -0
- package/benchmarks/memory-bench.ts +0 -109
- package/src/analytics/reduce.ts +0 -388
- package/src/analytics/schema-inference.ts +0 -217
- package/src/analytics/semantic-types.ts +0 -36
- package/src/analytics/stats.ts +0 -383
- package/src/analytics/validator.ts +0 -134
- package/src/cli/commands/cast.ts +0 -60
- package/src/cli/commands/convert.ts +0 -128
- package/src/cli/commands/filter.ts +0 -58
- package/src/cli/commands/inspect.ts +0 -193
- package/src/cli/commands/map.ts +0 -66
- package/src/cli/commands/reduce.ts +0 -74
- package/src/cli/commands/rename.ts +0 -59
- package/src/cli/commands/sample.ts +0 -61
- package/src/cli/commands/schema.ts +0 -86
- package/src/cli/commands/select.ts +0 -59
- package/src/cli/commands/stats.ts +0 -100
- package/src/cli/commands/validate.ts +0 -123
- package/src/cli/index.ts +0 -285
- package/src/core/batch.ts +0 -81
- package/src/core/errors.ts +0 -81
- package/src/core/pipeline.ts +0 -150
- package/src/core/types.ts +0 -110
- package/src/index.ts +0 -52
- package/src/readers/csv.ts +0 -302
- package/src/readers/index.ts +0 -88
- package/src/readers/json.ts +0 -256
- package/src/readers/jsonl.ts +0 -126
- package/src/readers/xlsx.ts +0 -182
- package/src/transforms/cast.ts +0 -212
- package/src/transforms/expression.ts +0 -1114
- package/src/transforms/filter.ts +0 -38
- package/src/transforms/map.ts +0 -99
- package/src/transforms/rename.ts +0 -47
- package/src/transforms/sample.ts +0 -57
- package/src/transforms/select.ts +0 -64
- package/src/utils/compression.ts +0 -43
- package/src/utils/formatting.ts +0 -75
- package/src/utils/progress.ts +0 -37
- package/src/writers/csv.ts +0 -106
- package/src/writers/index.ts +0 -87
- package/src/writers/json.ts +0 -52
- package/src/writers/jsonl.ts +0 -49
- package/src/writers/xlsx.ts +0 -87
- package/tests/analytics.test.ts +0 -119
- package/tests/cli-integration.test.ts +0 -187
- package/tests/map-reduce.test.ts +0 -223
- package/tests/readers-writers.test.ts +0 -189
- package/tests/transforms.test.ts +0 -199
- package/tsconfig.json +0 -21
- package/vitest.config.ts +0 -9
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rowpipe
|
|
3
|
+
description: Stream-first tabular data toolkit for inspecting, analyzing, transforming, validating, converting, mapping, and reducing large datasets across CSV, TSV, JSON, JSONL, XLSX, Apache Parquet, and Markdown formats with bounded O(1) memory and extreme throughput (~500,000 rows/s).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Rowpipe: Stream-First Tabular Data Toolkit
|
|
7
|
+
|
|
8
|
+
Rowpipe is a high-performance, stream-first command-line toolkit and Node.js library for tabular data processing. It processes gigabyte-scale datasets with bounded $O(1)$ memory usage, backpressure, batching, and zero-`eval` safe expression execution.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 🚀 Quick Command Reference
|
|
13
|
+
|
|
14
|
+
| Task | Command Syntax | Description |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| **Inspect** | `rowpipe inspect <file> [--sheet <name>] [--json]` | Inspect row count, column types, null %, and HLL uniqueness |
|
|
17
|
+
| **Convert** | `rowpipe convert <input> <output> [--all-sheets]` | Stream convert between CSV, TSV, JSON, JSONL, XLSX, Parquet, Markdown |
|
|
18
|
+
| **Schema** | `rowpipe schema <file> [--full] [--sample <n>]` | Infer column types and semantic patterns (email, url, uuid) |
|
|
19
|
+
| **Stats** | `rowpipe stats <file> [--column <name>]` | Streaming Welford numeric stats & HyperLogLog distinct count |
|
|
20
|
+
| **Map** | `rowpipe map <file> "<col1=expr1>" "<col2=expr2>"` | Derive and compute columns per row with arithmetic/JSON dot-notation |
|
|
21
|
+
| **Filter** | `rowpipe filter <file> "<expression>"` | Filter rows using safe expression engine and pipe syntax (`\|`) |
|
|
22
|
+
| **Reduce** | `rowpipe reduce <file> "<agg1=func(col)>" [--by <cols>]` | Global & group-by streaming aggregations (`sum`, `avg`, `min`, `max`, `count`, `countDistinct`, `stddev`) |
|
|
23
|
+
| **Select** | `rowpipe select <file> <col1,col2,...>` | Project subset of columns in order |
|
|
24
|
+
| **Rename** | `rowpipe rename <file> <old1=new1> <old2=new2>` | Rename columns in stream |
|
|
25
|
+
| **Cast** | `rowpipe cast <file> <col1:type> [--on-error null]` | Stream cast types (`integer`, `number`, `boolean`, `date`) |
|
|
26
|
+
| **Sample** | `rowpipe sample <file> --rows <n> [--seed <s>]` | Reservoir sampling with bounded memory |
|
|
27
|
+
| **Validate** | `rowpipe validate <file> --schema <schema.json>` | Validate stream against JSON schema definition |
|
|
28
|
+
| **Diff** | `rowpipe diff <left> <right> --key <cols> [opts]` | Stream compare datasets by key across formats with bounded RAM and disk spilling |
|
|
29
|
+
| **Files** | `rowpipe files <path> [--include <glob>] [opts]` | Stream filesystem directory as tabular records with bounded memory, globs, hashes, and MIME |
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 🛠️ Common Workflows & Examples
|
|
34
|
+
|
|
35
|
+
### 1. Inspect & Schema Inference (CSV, TSV, Parquet, Excel)
|
|
36
|
+
```bash
|
|
37
|
+
# Inspect CSV / TSV / JSONL file
|
|
38
|
+
rowpipe inspect dataset.csv
|
|
39
|
+
rowpipe inspect data.tsv
|
|
40
|
+
|
|
41
|
+
# Inspect Apache Parquet file directly
|
|
42
|
+
rowpipe inspect dataset.parquet --json
|
|
43
|
+
|
|
44
|
+
# Inspect multi-sheet Excel workbook
|
|
45
|
+
rowpipe inspect workbook.xlsx
|
|
46
|
+
rowpipe inspect workbook.xlsx --sheet Orders
|
|
47
|
+
|
|
48
|
+
# Full schema inference with semantic types (email, URL, UUID, date)
|
|
49
|
+
rowpipe schema huge.csv --full
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 2. Format Conversion, Parquet & Gzip Streams
|
|
53
|
+
```bash
|
|
54
|
+
# Convert CSV to Apache Parquet
|
|
55
|
+
rowpipe convert sales.csv sales.parquet
|
|
56
|
+
|
|
57
|
+
# Convert Parquet to compressed JSONL (.jsonl.gz)
|
|
58
|
+
rowpipe convert sales.parquet sales.jsonl.gz
|
|
59
|
+
|
|
60
|
+
# Filter Parquet and output directly as GitHub Markdown table
|
|
61
|
+
rowpipe filter dataset.parquet "amount > 500" --to markdown
|
|
62
|
+
|
|
63
|
+
# Convert specific Excel worksheet to TSV
|
|
64
|
+
rowpipe convert workbook.xlsx orders.tsv --sheet Orders
|
|
65
|
+
|
|
66
|
+
# Bulk export all worksheets in workbook to a directory
|
|
67
|
+
rowpipe convert workbook.xlsx --all-sheets --out-dir ./exported_sheets --to csv
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 3. Row Derivations & Calculations (`map`)
|
|
71
|
+
Derive calculated columns using arithmetic, strings, dates, and direct JSON dot-notation:
|
|
72
|
+
```bash
|
|
73
|
+
# Arithmetic & percentage formulas
|
|
74
|
+
rowpipe map sales.csv \
|
|
75
|
+
"profit = revenue - cost" \
|
|
76
|
+
"tax = revenue * 0.20" \
|
|
77
|
+
"margin = ((revenue - cost) / revenue) * 100"
|
|
78
|
+
|
|
79
|
+
# Direct JSON dot-notation & pipe transformations
|
|
80
|
+
rowpipe map events.jsonl \
|
|
81
|
+
"city = payload.user.address.city" \
|
|
82
|
+
"email = payload.user.email | lower | trim" \
|
|
83
|
+
"item_price = payload.items.0.price" \
|
|
84
|
+
"year = created_at | year"
|
|
85
|
+
|
|
86
|
+
# Conditional mapping
|
|
87
|
+
rowpipe map orders.csv "net_total = total | if(is_vip, total * 0.90, total) | round(2)"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 4. Advanced Row Filtering (`filter`)
|
|
91
|
+
```bash
|
|
92
|
+
# Arithmetic & comparison on Parquet / CSV
|
|
93
|
+
rowpipe filter sales.parquet '(revenue - cost) / revenue >= 0.25'
|
|
94
|
+
|
|
95
|
+
# Pipe syntax for string & date functions
|
|
96
|
+
rowpipe filter users.csv 'email | lower | endsWith("@corp.com")'
|
|
97
|
+
rowpipe filter orders.csv 'created_at | year == 2026 && total > 500'
|
|
98
|
+
|
|
99
|
+
# Direct JSON dot-notation in filter
|
|
100
|
+
rowpipe filter events.jsonl.gz 'payload.user.city == "Istanbul" && payload.items.0.price > 50'
|
|
101
|
+
|
|
102
|
+
# Set membership and range checks
|
|
103
|
+
rowpipe filter products.tsv 'category | in("Electronics", "Computers") && price | between(100, 1000)'
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 5. Group-By & Global Aggregations (`reduce`)
|
|
107
|
+
```bash
|
|
108
|
+
# 1. Global summary (Single row output)
|
|
109
|
+
rowpipe reduce sales.parquet \
|
|
110
|
+
"total_revenue = sum(revenue)" \
|
|
111
|
+
"avg_margin = avg(margin)" \
|
|
112
|
+
"min_price = min(price)" \
|
|
113
|
+
"max_price = max(price)" \
|
|
114
|
+
"total_orders = count()" \
|
|
115
|
+
"unique_customers = countDistinct(customer_id)"
|
|
116
|
+
|
|
117
|
+
# 2. Group-By Streaming Aggregations
|
|
118
|
+
rowpipe reduce sales.csv \
|
|
119
|
+
"total_revenue = sum(revenue)" \
|
|
120
|
+
"avg_profit = avg(profit)" \
|
|
121
|
+
"order_count = count()" \
|
|
122
|
+
--by country,category
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Supported reduce functions:
|
|
126
|
+
- `sum(col)` — Numeric sum
|
|
127
|
+
- `avg(col)` / `mean(col)` — Average
|
|
128
|
+
- `min(col)` / `max(col)` — Minimum / Maximum
|
|
129
|
+
- `count()` / `count(col)` — Row / non-null count
|
|
130
|
+
- `countDistinct(col)` — HyperLogLog distinct cardinality ($O(1)$ memory)
|
|
131
|
+
- `stddev(col)` / `variance(col)` — Welford single-pass online standard deviation / variance
|
|
132
|
+
- `first(col)` / `last(col)` — First non-null / last value
|
|
133
|
+
|
|
134
|
+
### 6. End-to-End Unix Pipe Processing Chain
|
|
135
|
+
```bash
|
|
136
|
+
cat transactions.csv.gz | \
|
|
137
|
+
rowpipe map - "profit=revenue - cost" "margin=((revenue - cost) / revenue) * 100" | \
|
|
138
|
+
rowpipe filter - "margin >= 15" | \
|
|
139
|
+
rowpipe reduce - "total_profit=sum(profit)" "avg_margin=avg(margin)" "count=count()" --by country | \
|
|
140
|
+
rowpipe convert - --to markdown
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 7. Dataset Diffing & Change Feeds (`diff`)
|
|
144
|
+
Compare two datasets by key across formats with bounded memory ($O(1)$) and automatic disk spilling:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
# 1. Basic Keyed Diff
|
|
148
|
+
rowpipe diff yesterday.csv today.csv --key id
|
|
149
|
+
|
|
150
|
+
# 2. Composite Key & Cross-Format (CSV vs Parquet)
|
|
151
|
+
rowpipe diff old.csv new.parquet --key country,user_id
|
|
152
|
+
|
|
153
|
+
# 3. Multi-Sheet Excel Workbook Diff
|
|
154
|
+
rowpipe diff old.xlsx new.xlsx --sheet Users --key id
|
|
155
|
+
|
|
156
|
+
# 4. Filter Columns & String Normalization
|
|
157
|
+
rowpipe diff old.csv new.csv \
|
|
158
|
+
--key id \
|
|
159
|
+
--ignore updated_at,last_seen \
|
|
160
|
+
--trim \
|
|
161
|
+
--ignore-case \
|
|
162
|
+
--epsilon 0.001
|
|
163
|
+
|
|
164
|
+
# 5. Output Modes: Rows, JSONL Patch, and JSON
|
|
165
|
+
rowpipe diff old.csv new.csv --key id --format rows --only changed --limit 50
|
|
166
|
+
rowpipe diff old.csv new.csv --key id --format patch > changes.jsonl
|
|
167
|
+
rowpipe diff old.csv new.csv --key id --json
|
|
168
|
+
|
|
169
|
+
# 6. CI/CD Validation (Fails with exit code 1 if differences found)
|
|
170
|
+
rowpipe diff expected.csv actual.csv --key id --fail-on-diff
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### 8. Filesystem as Streaming Tabular Records (`files`)
|
|
174
|
+
Stream files and directory trees with bounded memory, globs, streaming hashes, and MIME detection:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
# 1. Stream directory records
|
|
178
|
+
rowpipe files ./src --include "**/*.ts"
|
|
179
|
+
|
|
180
|
+
# 2. Filter large files and select columns via Unix pipe
|
|
181
|
+
rowpipe files ./uploads | rowpipe filter - 'size > 1000000' | rowpipe select - relative_path,size
|
|
182
|
+
|
|
183
|
+
# 3. Stream SHA-256 or fast hashes
|
|
184
|
+
rowpipe files ./assets --hash sha256 --to jsonl
|
|
185
|
+
rowpipe files ./dataset --hash fast
|
|
186
|
+
|
|
187
|
+
# 4. Generate deterministic build snapshot
|
|
188
|
+
rowpipe files snapshot ./dist --hash sha256 --to jsonl > dist.snapshot.jsonl
|
|
189
|
+
|
|
190
|
+
# 5. Direct directory diffing
|
|
191
|
+
rowpipe files diff ./dist-v1 ./dist-v2 --hash sha256
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## 💻 Node.js Library API
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
import {
|
|
200
|
+
createPipeline,
|
|
201
|
+
createReader,
|
|
202
|
+
createWriter,
|
|
203
|
+
CSVReader,
|
|
204
|
+
JSONLWriter,
|
|
205
|
+
ParquetReader,
|
|
206
|
+
ParquetWriter,
|
|
207
|
+
MarkdownWriter,
|
|
208
|
+
XLSXReader,
|
|
209
|
+
FileSystemReader,
|
|
210
|
+
mapRows,
|
|
211
|
+
filterRows,
|
|
212
|
+
reduceRows,
|
|
213
|
+
diffRows,
|
|
214
|
+
computeDiff,
|
|
215
|
+
} from "rowpipe";
|
|
216
|
+
import { createReadStream, createWriteStream } from "node:fs";
|
|
217
|
+
|
|
218
|
+
// 1. Streaming Parquet -> Filter -> Markdown Pipeline
|
|
219
|
+
const reader = new ParquetReader("sales.parquet");
|
|
220
|
+
const writer = new MarkdownWriter(createWriteStream("summary.md"));
|
|
221
|
+
|
|
222
|
+
await createPipeline(reader)
|
|
223
|
+
.pipe(mapRows({
|
|
224
|
+
profit: "revenue - cost",
|
|
225
|
+
margin: "((revenue - cost) / revenue) * 100",
|
|
226
|
+
}))
|
|
227
|
+
.pipe(filterRows("margin >= 15"))
|
|
228
|
+
.pipe(reduceRows({
|
|
229
|
+
by: ["country"],
|
|
230
|
+
aggregations: [
|
|
231
|
+
"total_profit = sum(profit)",
|
|
232
|
+
"avg_margin = avg(margin)",
|
|
233
|
+
"count = count()",
|
|
234
|
+
],
|
|
235
|
+
}))
|
|
236
|
+
.to(writer);
|
|
237
|
+
|
|
238
|
+
// 2. Stream Filesystem Directory as Tabular Rows
|
|
239
|
+
const fsReader = new FileSystemReader({
|
|
240
|
+
root: "./src",
|
|
241
|
+
include: ["**/*.ts"],
|
|
242
|
+
hash: "sha256",
|
|
243
|
+
mime: true,
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
for await (const batch of fsReader.read()) {
|
|
247
|
+
for (const file of batch.rows) {
|
|
248
|
+
console.log(`${file.relative_path} (${file.size} bytes) - Hash: ${file.hash}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// 3. Streaming Diff Events
|
|
253
|
+
for await (const event of diffRows({
|
|
254
|
+
leftPath: "old.csv",
|
|
255
|
+
rightPath: "new.parquet",
|
|
256
|
+
keys: ["id"],
|
|
257
|
+
ignore: ["updated_at"],
|
|
258
|
+
})) {
|
|
259
|
+
if (event.type === "changed") {
|
|
260
|
+
console.log(`Row ${JSON.stringify(event.key)} changed:`, event.changes);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
```
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { createPipeline } from "../src/core/pipeline.js";
|
|
2
|
-
import { filterRows } from "../src/transforms/filter.js";
|
|
3
|
-
import { selectColumns } from "../src/transforms/select.js";
|
|
4
|
-
import { castColumns } from "../src/transforms/cast.js";
|
|
5
|
-
import { DatasetStatsAggregator } from "../src/analytics/stats.js";
|
|
6
|
-
import type { DataBatch, DataStream, Row } from "../src/core/types.js";
|
|
7
|
-
import { formatBytes, formatNumber } from "../src/utils/formatting.js";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Generator producing synthetic data batches without buffering.
|
|
11
|
-
*/
|
|
12
|
-
async function* generateSyntheticStream(
|
|
13
|
-
totalRows: number,
|
|
14
|
-
batchSize = 2000
|
|
15
|
-
): DataStream {
|
|
16
|
-
let offset = 0;
|
|
17
|
-
while (offset < totalRows) {
|
|
18
|
-
const currentBatchSize = Math.min(batchSize, totalRows - offset);
|
|
19
|
-
const rows: Row[] = new Array(currentBatchSize);
|
|
20
|
-
|
|
21
|
-
for (let i = 0; i < currentBatchSize; i++) {
|
|
22
|
-
const id = offset + i;
|
|
23
|
-
rows[i] = {
|
|
24
|
-
id: String(id),
|
|
25
|
-
name: `User_${id}`,
|
|
26
|
-
age: String(20 + (id % 50)),
|
|
27
|
-
email: `user${id}@example.com`,
|
|
28
|
-
revenue: String((id % 1000) * 1.5),
|
|
29
|
-
country: id % 3 === 0 ? "TR" : id % 3 === 1 ? "US" : "DE",
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
yield {
|
|
34
|
-
rows,
|
|
35
|
-
offset,
|
|
36
|
-
};
|
|
37
|
-
offset += currentBatchSize;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async function runBenchmark(totalRows: number): Promise<void> {
|
|
42
|
-
process.stdout.write(`\n======================================================\n`);
|
|
43
|
-
process.stdout.write(`Running Benchmark with ${formatNumber(totalRows)} synthetic rows...\n`);
|
|
44
|
-
process.stdout.write(`Pipeline: Input -> Filter(age > 25) -> Select(id, name, age, revenue) -> Cast(revenue:number) -> Stats\n`);
|
|
45
|
-
process.stdout.write(`------------------------------------------------------\n`);
|
|
46
|
-
|
|
47
|
-
const initialMem = process.memoryUsage();
|
|
48
|
-
let peakRss = initialMem.rss;
|
|
49
|
-
let peakHeap = initialMem.heapUsed;
|
|
50
|
-
|
|
51
|
-
const startTime = Date.now();
|
|
52
|
-
let processedRows = 0;
|
|
53
|
-
|
|
54
|
-
const rawStream = generateSyntheticStream(totalRows, 2000);
|
|
55
|
-
|
|
56
|
-
const pipeline = createPipeline(rawStream)
|
|
57
|
-
.pipe(filterRows("age > 25"))
|
|
58
|
-
.pipe(selectColumns(["id", "name", "age", "revenue"]))
|
|
59
|
-
.pipe(castColumns({ revenue: "number" }));
|
|
60
|
-
|
|
61
|
-
const statsAgg = new DatasetStatsAggregator();
|
|
62
|
-
|
|
63
|
-
for await (const batch of pipeline.batches()) {
|
|
64
|
-
processedRows += batch.rows.length;
|
|
65
|
-
for (const row of batch.rows) {
|
|
66
|
-
statsAgg.add(row);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const currentMem = process.memoryUsage();
|
|
70
|
-
if (currentMem.rss > peakRss) peakRss = currentMem.rss;
|
|
71
|
-
if (currentMem.heapUsed > peakHeap) peakHeap = currentMem.heapUsed;
|
|
72
|
-
|
|
73
|
-
if (processedRows % 250000 === 0 || processedRows === totalRows) {
|
|
74
|
-
const elapsed = (Date.now() - startTime) / 1000 || 0.001;
|
|
75
|
-
const speed = Math.round(processedRows / elapsed);
|
|
76
|
-
process.stdout.write(
|
|
77
|
-
` Processed: ${formatNumber(processedRows)} rows | Elapsed: ${elapsed.toFixed(1)}s | RSS: ${formatBytes(currentMem.rss)} | Heap: ${formatBytes(currentMem.heapUsed)} | Speed: ${formatNumber(speed)} rows/s\n`
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const totalTimeMs = Date.now() - startTime;
|
|
83
|
-
const elapsedSec = totalTimeMs / 1000 || 0.001;
|
|
84
|
-
const rowsPerSec = Math.round(totalRows / elapsedSec);
|
|
85
|
-
// Estimate ~70 bytes per row
|
|
86
|
-
const estimatedMB = (totalRows * 70) / (1024 * 1024);
|
|
87
|
-
const mbPerSec = (estimatedMB / elapsedSec).toFixed(1);
|
|
88
|
-
|
|
89
|
-
const results = statsAgg.result();
|
|
90
|
-
|
|
91
|
-
process.stdout.write(`------------------------------------------------------\n`);
|
|
92
|
-
process.stdout.write(`Benchmark Results:\n`);
|
|
93
|
-
process.stdout.write(` Total Input Rows: ${formatNumber(totalRows)}\n`);
|
|
94
|
-
process.stdout.write(` Filtered Rows: ${formatNumber(processedRows)}\n`);
|
|
95
|
-
process.stdout.write(` Execution Time: ${elapsedSec.toFixed(2)}s\n`);
|
|
96
|
-
process.stdout.write(` Throughput: ${formatNumber(rowsPerSec)} rows/s (~${mbPerSec} MB/s)\n`);
|
|
97
|
-
process.stdout.write(` Initial RSS: ${formatBytes(initialMem.rss)}\n`);
|
|
98
|
-
process.stdout.write(` Peak RSS: ${formatBytes(peakRss)}\n`);
|
|
99
|
-
process.stdout.write(` Peak Heap Used: ${formatBytes(peakHeap)}\n`);
|
|
100
|
-
process.stdout.write(` Memory Growth: Bounded ($O(1)$ constant memory)\n`);
|
|
101
|
-
process.stdout.write(`======================================================\n`);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
async function main() {
|
|
105
|
-
await runBenchmark(100_000);
|
|
106
|
-
await runBenchmark(1_000_000);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
main().catch(console.error);
|