Documentation
¶
Overview ¶
Package parquetio reads and writes gobi Frames as Apache Parquet.
Compression is delegated to Parquet's built-in codecs. When a Frame contains geometry columns, WriteFile emits a GeoParquet 1.1 metadata blob under the Parquet file-level "geo" key; ReadFile re-hydrates it into the returned Frame's schema.
The reader offers two entry points:
ReadFile materializes the whole file as a single Frame. Peak memory is roughly the file's decompressed size. Good for small/medium files where you want the whole dataset at once.
ReadFileChunksFunc streams the file as record-batch-sized Frames. Only one batch's arrow buffers are live at a time, so peak memory is bounded regardless of source file size. Good for ETL / bounded- memory pipelines.
Both entry points accept an ReadOptions.Columns list to project the read to a subset of columns. Projected-away columns are neither fetched from disk nor decompressed nor materialized into arrow arrays.
Index ¶
- Constants
- Variables
- func ReadFile(path string, opts *ReadOptions) (*gobi.Frame, error)
- func ReadFileChunksFunc(path string, opts *ReadOptions, fn func(*gobi.Frame) error) error
- func ReadReader(r io.ReaderAt, size int64, opts *ReadOptions) (*gobi.Frame, error)
- func ReadReaderChunksFunc(r io.ReaderAt, size int64, opts *ReadOptions, fn func(*gobi.Frame) error) error
- func ReadSchema(path string, opts *ReadOptions) (*arrow.Schema, error)
- func ScanFile(path string, opts *ReadOptions) *gobi.LazyFrame
- func WriteFile(f *gobi.Frame, path string, opts *WriteOptions) error
- type Codec
- type ReadOptions
- type WriteOptions
Constants ¶
const DefaultChunkRows = 64 * 1024
DefaultChunkRows is the arrow record-batch size used by ReadFileChunksFunc when ReadOptions.ChunkRows is 0.
Variables ¶
var ( ErrUnknownCodec = errors.New("parquetio: unknown compression codec") ErrColumnNotFound = errors.New("parquetio: column not found") ErrChunksAborted = errors.New("parquetio: chunk callback returned error") )
Errors.
Functions ¶
func ReadFile ¶
func ReadFile(path string, opts *ReadOptions) (*gobi.Frame, error)
ReadFile reads path into a single Frame. If opts.Columns is non-empty, only those columns are fetched + decoded. If the file has a GeoParquet "geo" key, it is re-attached to the Frame's Arrow schema so downstream code can detect geometry columns.
func ReadFileChunksFunc ¶
ReadFileChunksFunc streams path as record-batch-sized Frames. fn is invoked once per batch (~DefaultChunkRows rows by default; override via ReadOptions.ChunkRows). Only the current batch's arrow buffers are in memory, so peak footprint is bounded to roughly one batch.
The Frame handed to fn is Released after fn returns. To retain a Frame past the callback, call frame.Retain() inside fn and match with a frame.Release() when you're done with it.
If fn returns an error, iteration stops and the error is wrapped in ErrChunksAborted so callers can errors.Is / errors.As it. Underlying parquet read errors are returned directly.
func ReadReader ¶ added in v0.2.0
ReadReader is the io.ReaderAt-backed counterpart to ReadFile. Reads a Parquet file from any random-access byte source (S3 GetObject with Range headers, in-memory bytes.Reader, etc.) whose size is known upfront. Materializes the whole payload as a single Frame — memory footprint mirrors ReadFile.
The caller retains ownership of r; ReadReader does not Close it. Pass io.ReaderAt + Size explicitly rather than requiring io.Seeker so callers implementing thin S3 / HTTP shims don't need to fake seeking against a known length.
GeoParquet metadata + column projection + predicate pushdown work the same as ReadFile — ReadOptions is honored uniformly.
func ReadReaderChunksFunc ¶ added in v0.2.0
func ReadReaderChunksFunc(r io.ReaderAt, size int64, opts *ReadOptions, fn func(*gobi.Frame) error) error
ReadReaderChunksFunc is the io.ReaderAt-backed counterpart to ReadFileChunksFunc. Streams the Parquet payload as record-batch-sized Frames without materializing the whole file. Batch lifetime + error semantics mirror the path-based version.
The caller retains ownership of r; ReadReaderChunksFunc does not Close it.
func ReadSchema ¶
func ReadSchema(path string, opts *ReadOptions) (*arrow.Schema, error)
ReadSchema opens path, reads just the parquet footer, and returns the arrow schema of the file — projected through opts.Columns and stamped with the GeoParquet "geo" metadata if present.
Reads no column data. Used by ScanFile to populate a lazy plan node's output schema without materializing any rows.
func ScanFile ¶
func ScanFile(path string, opts *ReadOptions) *gobi.LazyFrame
ScanFile returns a LazyFrame anchored at a parquet scan. No data is read until Collect() is called; the schema is read eagerly from the parquet footer so downstream nodes can propagate types.
If the file can't be opened at construction (missing file, bad footer, unknown codec), the returned LazyFrame still builds — the error surfaces at Collect. This matches DuckDB's / Polars' `scan_parquet` semantics: cheap to compose, errors bubble at materialization.
Composes with the LazyFrame chain: Filter, Select, WithColumn, SortBy, GroupBy.Agg, Join, Limit, Head, Tail, DropColumn.
A future optimizer will push Filter and Select nodes above the scan back INTO the parquet reader (predicate + projection pushdown, bloom-filter-driven rowgroup skipping). Today ScanFile is pure API shape — it reads the whole file at Collect regardless of what's above it.
func WriteFile ¶
func WriteFile(f *gobi.Frame, path string, opts *WriteOptions) error
WriteFile writes f to path. A nil opts uses defaults: CodecSnappy compression and parquet-arrow's default row-group sizing (~1M rows).
If f contains any geometry columns, the output includes a GeoParquet 1.1 metadata blob under the file-level "geo" key.
Tuning row-group size matters for readers that use rowgroup statistics for predicate pushdown or that stream one rowgroup at a time. Smaller groups → more granular filter skipping and lower per-batch memory; larger groups → better compression ratios and less per-group overhead. The parquet default is a reasonable starting point for most workloads.
Types ¶
type Codec ¶
type Codec string
Codec identifies a Parquet-level compression codec.
func ParseCodec ¶
ParseCodec resolves a codec by name (case-insensitive). Empty and "none" map to CodecUncompressed.
type ReadOptions ¶
type ReadOptions struct {
// Columns projects the file to a subset of top-level columns by
// name. nil or empty = read all columns.
//
// Names not present in the file's schema return ErrColumnNotFound.
// Column projection is applied at the parquet reader layer: the
// excluded columns are never fetched, decompressed, or materialized
// into arrow arrays. The savings scale with how large those columns
// are relative to the file — narrow analytical files where the caller
// wants a few columns out of many benefit most.
Columns []string
// ChunkRows is the arrow record-batch size used by
// ReadFileChunksFunc. Each RecordReader.Next() call produces at
// most ChunkRows rows. 0 = DefaultChunkRows. Ignored by ReadFile.
//
// Sub-partitioning a row group into fixed-size batches is what
// bounds streaming memory to ~one batch at a time regardless of
// the file's row-group sizes.
ChunkRows int
// Allocator overrides the Arrow allocator. nil = memory.DefaultAllocator.
Allocator memory.Allocator
// Predicate is a hint from the optimizer for row-group skipping.
// When set, ReadFile / ReadFileChunksFunc walk each row-group's
// footer statistics and skip whole groups whose (min, max) bounds
// prove no row could satisfy the predicate. The Filter operation
// above the read still runs — this is a coarse fast-path that
// avoids fetching irrelevant row-groups off disk.
//
// Predicates only prune when they reference columns present in
// the file's schema. Unrecognized columns silently prevent
// pruning (conservative — a "maybe" survives). Uses the same
// Expr type as Frame.FilterExpr.
Predicate gobi.Expr
// RowGroups optionally restricts the read to a specific set of
// row-group indices. When set, ReadFile / ReadFileChunksFunc /
// ScanFile process only those row-groups; when nil or empty,
// all row-groups are read.
//
// Primarily used internally to partition scans across parallel
// workers (see ScanWorkers) — each worker gets a disjoint
// RowGroups slice. Callers can set it directly for very
// targeted reads (e.g. "just the last row-group of this file"),
// though the more common way to restrict is via Columns or
// Predicate.
RowGroups []int
// ScanWorkers controls row-group-level parallelism for ScanFile.
// 0 (default) = runtime.GOMAXPROCS(0), capped at NumRowGroups.
// 1 = single-threaded (the previous behavior). n > 1 = n
// workers, capped at NumRowGroups.
//
// Ignored by ReadFile (always single-threaded — reads one whole
// file) and by ReadFileChunksFunc (also single-threaded — the
// callback API is fundamentally serial). Applies only when the
// scan flows through the Layer 6 executor via ScanFile +
// LazyFrame.Collect.
ScanWorkers int
}
ReadOptions controls parquet read behavior. A nil pointer is treated as the zero value.
type WriteOptions ¶
type WriteOptions struct {
// Codec selects the Parquet page compression codec. Empty string
// defaults to CodecSnappy — matches parquet-arrow's own default
// and is the common choice for good balance between size and
// decode speed.
Codec Codec
// RowGroupRows caps the maximum number of rows per row group. 0
// uses parquet-arrow's default (~1M rows).
//
// Smaller row groups → more granular predicate pushdown (readers
// can skip whole groups via rowgroup statistics) and lower peak
// memory when streaming one group at a time. Larger row groups →
// better compression ratios and less per-group metadata overhead.
// 64k–256k is a reasonable range for analytical workloads that
// filter on min/max stats; leave at 0 for archive/bulk-load files
// where read patterns are full-scan.
RowGroupRows int64
// BloomFilterColumns names columns that should have a bloom
// filter attached to each row group. High-cardinality equality-
// filtered columns (user IDs, hashes, categorical keys) benefit
// most; skew-free min/max distributions do not — parquet's row-
// group statistics already handle those.
//
// gobi's own reader does not yet consume bloom filters for row-
// group skipping (that lands with the query optimizer). Files
// produced here are still consumed correctly by DuckDB, Spark,
// Polars, and pyarrow, which do use bloom filters for predicate
// pushdown on equality filters.
BloomFilterColumns []string
// BloomFilterFPP is the target false-positive probability for
// the bloom filters written above. 0 uses arrow-go's default
// (0.05). Lower FPP → larger filter on disk; reasonable range
// 0.01–0.1. Ignored when BloomFilterColumns is empty.
BloomFilterFPP float64
}
WriteOptions controls parquet write behavior. A nil pointer is treated as the zero value (CodecSnappy + parquet-arrow's default row-group sizing).