@@ -293,6 +293,94 @@ java -jar cli/target/vortex-cli-*-all.jar filter data.vortex "price >= 100" > ou
293293
294294---
295295
296+ # # Write and read a Map column
297+
298+ ` DType.Map` has no dedicated write-side value type. Physically, a map column' s `vortex.map`
299+ node has exactly one child — `entries`, a `ListView<Struct{key, value}>` — so you hand
300+ `writeChunk` the same shape you' d hand a plain ` ListView< Struct> ` column: a ` ListViewData`
301+ whose ` elements` is a ` StructData` of a keys array and a values array. See
302+ [explanation.md#map-column-layout](explanation.md#map-column-layout) for why the wire format
303+ looks like this and how the two independent nullability slots (map row vs. entry value) work.
304+
305+ ** Write:**
306+
307+ ` ` ` java
308+ import io.github.dfa1.vortex.core.model.ColumnName;
309+ import io.github.dfa1.vortex.core.model.DType;
310+ import io.github.dfa1.vortex.writer.encode.ListViewData;
311+ import io.github.dfa1.vortex.writer.encode.NullableData;
312+ import io.github.dfa1.vortex.writer.encode.StructData;
313+
314+ // map< utf8, i64? > — non-nullable string keys, nullable long values
315+ DType.Map mapType = new DType.Map(DType.UTF8, DType.I64.asNullable (), false, false);
316+ DType.Struct schema = new DType.Struct(List.of(ColumnName.of(" attrs" )), List.of(mapType), false);
317+
318+ // 3 rows: {a:1, b:2}, {} (empty map), {c:null}
319+ String[] keys = {" a" , " b" , " c" };
320+ long[] values = {1L, 2L, 0L}; // placeholder at the null entry
321+ boolean[] valueValidity = {true, true, false}; // per-entry value validity
322+ StructData entryStructs = new StructData(List.of(keys, new NullableData(values, valueValidity)));
323+
324+ int[] offsets = {0, 2, 2}; // row i' s entries start at entryStructs[offsets[i]]
325+ int[] sizes = {2, 0, 1}; // row i has sizes[i] entries
326+ ListViewData column = new ListViewData(entryStructs, offsets, sizes, 3);
327+
328+ try (var ch = FileChannel.open(Path.of("attrs.vortex"), StandardOpenOption.CREATE, StandardOpenOption.WRITE);
329+ var writer = VortexWriter.create(ch, schema, WriteOptions.defaults())) {
330+ writer.writeChunk(Map.of(ColumnName.of("attrs"), column));
331+ }
332+ ```
333+
334+ A *nullable map row* (as opposed to a nullable value inside a present map) wraps the whole
335+ `ListViewData` in `NullableData` instead — `mapType.asNullable()` in the schema, and
336+ `new NullableData(column, new boolean[]{true, false, true})` in place of `column` above.
337+
338+ **Read:**
339+
340+ ```java
341+ import io.github.dfa1.vortex.reader.array.IntArray;
342+ import io.github.dfa1.vortex.reader.array.ListViewArray;
343+ import io.github.dfa1.vortex.reader.array.MapArray;
344+ import io.github.dfa1.vortex.reader.array.MaskedArray;
345+ import io.github.dfa1.vortex.reader.array.StructArray;
346+ import io.github.dfa1.vortex.reader.array.VarBinArray;
347+
348+ try (var reader = VortexReader.open(Path.of("attrs.vortex"));
349+ var iter = reader.scan(ScanOptions.all())) {
350+ while (iter.hasNext()) {
351+ try (var chunk = iter.next()) {
352+ MapArray map = chunk.column("attrs");
353+
354+ // If the map itself is nullable, entries() is a MaskedArray; unwrap it first.
355+ var entries = map.entries() instanceof MaskedArray masked
356+ ? (ListViewArray) masked.inner() : (ListViewArray) map.entries();
357+ StructArray entryStructs = (StructArray) entries.elements();
358+ VarBinArray keys = (VarBinArray) entryStructs.field("key");
359+ var values = entryStructs.field("value"); // MaskedArray, since the value type is nullable here
360+ // A file written by vortex-java' s own writer always emits I32 offsets/sizes; a file
361+ // from another producer (e.g. the Rust reference) may pick a narrower or wider integer
362+ // width, so switch on the concrete Array subtype there instead of casting to IntArray.
363+ IntArray offsets = (IntArray) entries.offsets ();
364+ IntArray sizes = (IntArray) entries.sizes ();
365+
366+ for (long row = 0; row < map.length (); row++) {
367+ long start = offsets.getInt(row);
368+ long end = start + sizes.getInt(row);
369+ for (long i = start; i < end; i++) {
370+ // keys.getBytes(i) / values at index i are this row' s i-th {key, value} pair
371+ }
372+ }
373+ }
374+ }
375+ }
376+ ```
377+
378+ `ScanOptions.all()`/CLI `inspect` show `vortex.map` in a file' s layout tree as a ` vortex.listview`
379+ child under the ` vortex.map` node — see ` docs/reference.md#core-types` for ` DType.Map` ' s full
380+ field list (`keyType`, `valueType`, `keysSorted`, `nullable`) and `entriesDtype()`.
381+
382+ ---
383+
296384## Read files with unknown encodings
297385
298386By default, a file containing an unrecognized encoding ID throws `VortexException`.
0 commit comments