Skip to content

Commit 7292de2

Browse files
dfa1claude
andcommitted
feat(cli): add count and select subcommands
count reads row count from layout metadata (no data scan). select projects columns via ScanOptions and streams CSV to stdout. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 7219714 commit 7292de2

6 files changed

Lines changed: 102 additions & 9 deletions

File tree

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package io.github.dfa1.vortex.cli;
2+
3+
import io.github.dfa1.vortex.io.VortexReader;
4+
5+
import java.io.IOException;
6+
import java.nio.file.Files;
7+
import java.nio.file.Path;
8+
9+
final class CountCommand {
10+
11+
private CountCommand() {
12+
}
13+
14+
static int run(String[] args) {
15+
if (args.length != 2) {
16+
System.err.println("usage: count <file.vortex>");
17+
return 1;
18+
}
19+
Path path = Path.of(args[1]);
20+
if (!Files.exists(path)) {
21+
System.err.println("file not found: " + path);
22+
return 2;
23+
}
24+
try (VortexReader reader = VortexReader.open(path)) {
25+
System.out.println(reader.layout().rowCount());
26+
return 0;
27+
} catch (IOException e) {
28+
System.err.println("error: " + e.getMessage());
29+
return 3;
30+
}
31+
}
32+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package io.github.dfa1.vortex.cli;
2+
3+
import io.github.dfa1.vortex.csv.CsvExporter;
4+
import io.github.dfa1.vortex.csv.ExportOptions;
5+
6+
import java.io.IOException;
7+
import java.io.OutputStreamWriter;
8+
import java.io.Writer;
9+
import java.nio.charset.StandardCharsets;
10+
import java.nio.file.Files;
11+
import java.nio.file.Path;
12+
import java.util.Arrays;
13+
import java.util.List;
14+
15+
final class SelectCommand {
16+
17+
private SelectCommand() {
18+
}
19+
20+
static int run(String[] args) {
21+
if (args.length < 3) {
22+
System.err.println("usage: select <file.vortex> <col1> [col2 ...]");
23+
return 1;
24+
}
25+
Path path = Path.of(args[1]);
26+
if (!Files.exists(path)) {
27+
System.err.println("file not found: " + path);
28+
return 2;
29+
}
30+
List<String> columns = Arrays.asList(args).subList(2, args.length);
31+
ExportOptions options = ExportOptions.defaults().withColumns(columns);
32+
try {
33+
Writer stdout = new OutputStreamWriter(System.out, StandardCharsets.UTF_8);
34+
CsvExporter.exportCsv(path, stdout, options);
35+
stdout.flush();
36+
return 0;
37+
} catch (IOException e) {
38+
System.err.println("error: " + e.getMessage());
39+
return 3;
40+
}
41+
}
42+
}

cli/src/main/java/io/github/dfa1/vortex/cli/VortexCli.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ public static void main(String[] args) {
2020
case "export" -> ExportCommand.run(args);
2121
case "import" -> ImportCommand.run(args);
2222
case "schema" -> SchemaCommand.run(args);
23+
case "count" -> CountCommand.run(args);
24+
case "select" -> SelectCommand.run(args);
2325
default -> {
2426
System.err.println("unknown subcommand: " + args[0]);
2527
printUsage(System.err);
@@ -31,9 +33,11 @@ public static void main(String[] args) {
3133

3234
static void printUsage(PrintStream out) {
3335
out.println("Usage: java -jar vortex.jar <subcommand> [args]");
34-
out.println(" inspect <file.vortex> print file structure");
35-
out.println(" export <file.vortex> write CSV to stdout");
36-
out.println(" import <file.csv> [out.vortex] convert CSV to Vortex");
37-
out.println(" schema <file.vortex> print dtype (machine-readable)");
36+
out.println(" inspect <file.vortex> print file structure");
37+
out.println(" export <file.vortex> write CSV to stdout");
38+
out.println(" import <file.csv> [out.vortex] convert CSV to Vortex");
39+
out.println(" schema <file.vortex> print dtype (machine-readable)");
40+
out.println(" count <file.vortex> print row count");
41+
out.println(" select <file.vortex> <col> [...] project columns to CSV on stdout");
3842
}
3943
}

csv/src/main/java/io/github/dfa1/vortex/csv/CsvExporter.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,18 @@ private static void export(VortexReader reader, CsvWriter csvWriter, ExportOptio
6666
if (!(reader.dtype() instanceof DType.Struct schema)) {
6767
throw new VortexException("only struct root dtype supported for CSV export");
6868
}
69-
List<String> colNames = schema.fieldNames();
69+
List<String> colNames = options.hasProjection() ? options.columns() : schema.fieldNames();
7070
int colCount = colNames.size();
7171

7272
if (options.writeHeader()) {
7373
csvWriter.writeRecord(colNames);
7474
}
7575

76+
ScanOptions scanOptions = options.hasProjection()
77+
? new ScanOptions(colNames, null, ScanOptions.NO_LIMIT)
78+
: ScanOptions.all();
7679
String[] row = new String[colCount];
77-
try (ScanIterator iter = reader.scan(ScanOptions.all())) {
80+
try (ScanIterator iter = reader.scan(scanOptions)) {
7881
while (iter.hasNext()) {
7982
ScanResult chunk = iter.next();
8083
Array[] arrays = new Array[colCount];
Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
11
package io.github.dfa1.vortex.csv;
22

3+
import java.util.List;
4+
35
/// Options controlling Vortex → CSV export.
46
public record ExportOptions(
57
char delimiter,
6-
boolean writeHeader
8+
boolean writeHeader,
9+
List<String> columns
710
) {
811
public static ExportOptions defaults() {
9-
return new ExportOptions(',', true);
12+
return new ExportOptions(',', true, List.of());
13+
}
14+
15+
/// Restrict output to specific columns (projection). Empty list = all columns.
16+
public ExportOptions withColumns(List<String> cols) {
17+
return new ExportOptions(delimiter, writeHeader, List.copyOf(cols));
18+
}
19+
20+
public boolean hasProjection() {
21+
return !columns.isEmpty();
1022
}
1123
}

csv/src/test/java/io/github/dfa1/vortex/csv/CsvExporterTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ void suppressesHeaderWhenConfigured(@TempDir Path tmp) throws Exception {
8484
Path csv = tmp.resolve("out.csv");
8585

8686
// When
87-
CsvExporter.exportCsv(vortex, csv, new ExportOptions(',', false));
87+
CsvExporter.exportCsv(vortex, csv, new ExportOptions(',', false, java.util.List.of()));
8888

8989
// Then
9090
List<String> lines = Files.readAllLines(csv);

0 commit comments

Comments
 (0)