Documentation
¶
Overview ¶
Package arreflect provides utilities for converting between Apache Arrow arrays and Go structs using reflection.
The primary entry points are the generic functions At, ToSlice, FromSlice, RecordToSlice, and RecordFromSlice, which convert between Arrow arrays/records and Go slices of structs.
Schema inference is available via InferSchema and InferType.
Arrow struct tags control field mapping:
type MyRow struct {
Name string `arrow:"name"`
Score float64 `arrow:"score"`
Skip string `arrow:"-"`
Enc string `arrow:"enc,dict"`
T32 time.Time `arrow:"t32,time32"`
}
Temporal type overrides for time.Time fields:
arrow:"field,date32" — use Date32 instead of Timestamp arrow:"field,date64" — use Date64 instead of Timestamp arrow:"field,time32" — use Time32(ms) instead of Timestamp arrow:"field,time64" — use Time64(ns) instead of Timestamp
Additional tag options:
arrow:"field,view" — use STRING_VIEW/BINARY_VIEW for string/bytes fields, or LIST_VIEW for slice fields arrow:"field,ree" — run-end encoding at top-level only (struct fields not supported) arrow:"field,decimal(precision,scale)" — override decimal precision and scale (e.g., arrow:",decimal(18,2)")
Index ¶
- Variables
- func At[T any](arr arrow.Array, i int) (T, error)
- func AtAny(arr arrow.Array, i int) (any, error)
- func FromSlice[T any](vals []T, mem memory.Allocator, opts ...Option) (arrow.Array, error)
- func InferGoType(dt arrow.DataType) (reflect.Type, error)
- func InferSchema[T any]() (*arrow.Schema, error)
- func InferType[T any]() (arrow.DataType, error)
- func RecordAt[T any](rec arrow.RecordBatch, i int) (T, error)
- func RecordAtAny(rec arrow.RecordBatch, i int) (any, error)
- func RecordFromSlice[T any](vals []T, mem memory.Allocator, opts ...Option) (arrow.RecordBatch, error)
- func RecordToAnySlice(rec arrow.RecordBatch) ([]any, error)
- func RecordToSlice[T any](rec arrow.RecordBatch) ([]T, error)
- func ToAnySlice(arr arrow.Array) ([]any, error)
- func ToSlice[T any](arr arrow.Array) ([]T, error)
- type Option
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrUnsupportedType = errors.New("arreflect: unsupported type") ErrTypeMismatch = errors.New("arreflect: type mismatch") )
Functions ¶
func At ¶
Example ¶
package main
import (
"fmt"
"github.com/apache/arrow-go/v18/arrow/array"
"github.com/apache/arrow-go/v18/arrow/array/arreflect"
"github.com/apache/arrow-go/v18/arrow/memory"
)
func main() {
mem := memory.NewGoAllocator()
b := array.NewStringBuilder(mem)
defer b.Release()
b.Append("alpha")
b.Append("beta")
b.Append("gamma")
arr := b.NewArray()
defer arr.Release()
val, err := arreflect.At[string](arr, 1)
if err != nil {
panic(err)
}
fmt.Println(val)
}
Output: beta
func AtAny ¶
AtAny converts a single element at index i of an Arrow array to a Go value, inferring the Go type from the Arrow DataType at runtime via InferGoType. Useful when the column type is not known at compile time. Null elements are returned as the Go zero value of the inferred type; use arr.IsNull(i) to distinguish a null element from a genuine zero. For typed access when T is known, prefer At.
func FromSlice ¶
Example ¶
package main
import (
"fmt"
"github.com/apache/arrow-go/v18/arrow/array"
"github.com/apache/arrow-go/v18/arrow/array/arreflect"
"github.com/apache/arrow-go/v18/arrow/memory"
)
func main() {
mem := memory.NewGoAllocator()
arr, err := arreflect.FromSlice([]int32{10, 20, 30}, mem)
if err != nil {
panic(err)
}
defer arr.Release()
fmt.Println("Type:", arr.DataType())
fmt.Println("Len:", arr.Len())
for i := 0; i < arr.Len(); i++ {
fmt.Println(arr.(*array.Int32).Value(i))
}
}
Output: Type: int32 Len: 3 10 20 30
Example (LargeStruct) ¶
package main
import (
"fmt"
"github.com/apache/arrow-go/v18/arrow/array"
"github.com/apache/arrow-go/v18/arrow/array/arreflect"
"github.com/apache/arrow-go/v18/arrow/memory"
)
func main() {
type Event struct {
Name string `arrow:"name,large"`
Code int32 `arrow:"code"`
}
schema, err := arreflect.InferSchema[Event]()
if err != nil {
panic(err)
}
fmt.Println("Schema:", schema)
mem := memory.NewGoAllocator()
arr, err := arreflect.FromSlice([]Event{{"click", 1}, {"view", 2}}, mem)
if err != nil {
panic(err)
}
defer arr.Release()
sa := arr.(*array.Struct)
fmt.Println("Name type:", sa.Field(0).DataType())
fmt.Println("Code type:", sa.Field(1).DataType())
}
Output: Schema: schema: fields: 2 - name: type=large_utf8 - code: type=int32 Name type: large_utf8 Code type: int32
Example (StructSlice) ¶
package main
import (
"fmt"
"github.com/apache/arrow-go/v18/arrow/array"
"github.com/apache/arrow-go/v18/arrow/array/arreflect"
"github.com/apache/arrow-go/v18/arrow/memory"
)
func main() {
mem := memory.NewGoAllocator()
type Row struct {
Name string `arrow:"name"`
Score float64 `arrow:"score"`
}
arr, err := arreflect.FromSlice([]Row{
{"alice", 9.5},
{"bob", 7.0},
}, mem)
if err != nil {
panic(err)
}
defer arr.Release()
sa := arr.(*array.Struct)
fmt.Println("Type:", sa.DataType())
fmt.Println("Names:", sa.Field(0))
fmt.Println("Scores:", sa.Field(1))
}
Output: Type: struct<name: utf8, score: float64> Names: ["alice" "bob"] Scores: [9.5 7]
Example (ViewStruct) ¶
package main
import (
"fmt"
"github.com/apache/arrow-go/v18/arrow/array/arreflect"
)
func main() {
type Event struct {
Name string `arrow:"name,view"`
Code int32 `arrow:"code"`
}
schema, err := arreflect.InferSchema[Event]()
if err != nil {
panic(err)
}
fmt.Println("Schema:", schema)
}
Output: Schema: schema: fields: 2 - name: type=string_view - code: type=int32
Example (WithDecimal) ¶
package main
import (
"fmt"
"github.com/apache/arrow-go/v18/arrow/array/arreflect"
"github.com/apache/arrow-go/v18/arrow/decimal128"
"github.com/apache/arrow-go/v18/arrow/memory"
)
func main() {
mem := memory.NewGoAllocator()
vals := []decimal128.Num{
decimal128.FromI64(12345),
decimal128.FromI64(-67890),
}
arr, err := arreflect.FromSlice(vals, mem, arreflect.WithDecimal(10, 2))
if err != nil {
panic(err)
}
defer arr.Release()
fmt.Println("Type:", arr.DataType())
fmt.Println("Len:", arr.Len())
}
Output: Type: decimal(10, 2) Len: 2
Example (WithDict) ¶
package main
import (
"fmt"
"github.com/apache/arrow-go/v18/arrow/array"
"github.com/apache/arrow-go/v18/arrow/array/arreflect"
"github.com/apache/arrow-go/v18/arrow/memory"
)
func main() {
mem := memory.NewGoAllocator()
arr, err := arreflect.FromSlice(
[]string{"red", "green", "red", "blue", "green"},
mem,
arreflect.WithDict(),
)
if err != nil {
panic(err)
}
defer arr.Release()
fmt.Println("Type:", arr.DataType())
dict := arr.(*array.Dictionary)
fmt.Println("Indices:", dict.Indices())
fmt.Println("Dictionary:", dict.Dictionary())
}
Output: Type: dictionary<values=utf8, indices=int32, ordered=false> Indices: [0 1 0 2 1] Dictionary: ["red" "green" "blue"]
func InferGoType ¶
InferGoType returns the Go reflect.Type corresponding to the given Arrow DataType. For STRUCT types it constructs an anonymous struct type at runtime using reflect.StructOf; field names are exported (capitalised) with the original Arrow field name preserved in an arrow struct tag. Nullable Arrow fields (field.Nullable == true) become pointer types (*T). For DICTIONARY and RUN_END_ENCODED types it returns the Go type of the value/encoded type respectively (dictionaries are resolved transparently).
func InferSchema ¶
InferSchema infers an *arrow.Schema from a Go struct type T. T must be a struct type; returns an error otherwise. For column-level Arrow type inspection, use InferType. Field names come from arrow struct tags or Go field names. Pointer fields are marked Nullable=true.
Example ¶
package main
import (
"fmt"
"github.com/apache/arrow-go/v18/arrow/array/arreflect"
)
func main() {
type Event struct {
ID int64 `arrow:"id"`
Name string `arrow:"name"`
Score float64 `arrow:"score"`
Comment *string `arrow:"comment"`
}
schema, err := arreflect.InferSchema[Event]()
if err != nil {
panic(err)
}
fmt.Println(schema)
}
Output: schema: fields: 4 - id: type=int64 - name: type=utf8 - score: type=float64 - comment: type=utf8, nullable
func InferType ¶
InferType infers the Arrow DataType for a Go type T. For struct types, InferSchema is preferred when the result will be used with arrow.Record or array.NewRecord; InferType returns an arrow.DataType that would require an additional cast to *arrow.StructType.
func RecordAt ¶
func RecordAt[T any](rec arrow.RecordBatch, i int) (T, error)
RecordAt converts the row at index i of an Arrow Record to a Go value of type T. T must be a struct type whose fields correspond to the record's columns.
func RecordAtAny ¶
func RecordAtAny(rec arrow.RecordBatch, i int) (any, error)
RecordAtAny converts the row at index i of an Arrow Record to a Go value, inferring the Go type from the record's schema at runtime via InferGoType. Equivalent to AtAny on the struct array underlying the record.
func RecordFromSlice ¶
func RecordFromSlice[T any](vals []T, mem memory.Allocator, opts ...Option) (arrow.RecordBatch, error)
Example ¶
package main
import (
"fmt"
"github.com/apache/arrow-go/v18/arrow/array/arreflect"
"github.com/apache/arrow-go/v18/arrow/memory"
)
type Measurement struct {
Sensor string `arrow:"sensor"`
Value float64 `arrow:"value"`
}
func main() {
mem := memory.NewGoAllocator()
rows := []Measurement{
{"temp-1", 23.5},
{"temp-2", 19.8},
}
rec, err := arreflect.RecordFromSlice(rows, mem)
if err != nil {
panic(err)
}
defer rec.Release()
fmt.Println("Schema:", rec.Schema())
fmt.Println("Rows:", rec.NumRows())
fmt.Println("Col 0:", rec.Column(0))
fmt.Println("Col 1:", rec.Column(1))
}
Output: Schema: schema: fields: 2 - sensor: type=utf8 - value: type=float64 Rows: 2 Col 0: ["temp-1" "temp-2"] Col 1: [23.5 19.8]
func RecordToAnySlice ¶
func RecordToAnySlice(rec arrow.RecordBatch) ([]any, error)
RecordToAnySlice converts all rows of an Arrow Record to Go values, inferring the Go type at runtime via InferGoType. Equivalent to ToAnySlice on the struct array underlying the record.
func RecordToSlice ¶
func RecordToSlice[T any](rec arrow.RecordBatch) ([]T, error)
Example ¶
package main
import (
"fmt"
"github.com/apache/arrow-go/v18/arrow/array/arreflect"
"github.com/apache/arrow-go/v18/arrow/memory"
)
type Measurement struct {
Sensor string `arrow:"sensor"`
Value float64 `arrow:"value"`
}
func main() {
mem := memory.NewGoAllocator()
rows := []Measurement{
{"temp-1", 23.5},
{"temp-2", 19.8},
}
rec, err := arreflect.RecordFromSlice(rows, mem)
if err != nil {
panic(err)
}
defer rec.Release()
got, err := arreflect.RecordToSlice[Measurement](rec)
if err != nil {
panic(err)
}
for _, m := range got {
fmt.Printf("%s: %.1f\n", m.Sensor, m.Value)
}
}
Output: temp-1: 23.5 temp-2: 19.8
func ToAnySlice ¶
ToAnySlice converts all elements of an Arrow array to Go values, inferring the Go type from the Arrow DataType at runtime via InferGoType. All elements share the same inferred Go type. Null elements are returned as the Go zero value of the inferred type; use arr.IsNull(i) to distinguish a null element from a genuine zero value. For typed access when T is known, prefer ToSlice.
Example ¶
package main
import (
"fmt"
"github.com/apache/arrow-go/v18/arrow"
"github.com/apache/arrow-go/v18/arrow/array"
"github.com/apache/arrow-go/v18/arrow/array/arreflect"
"github.com/apache/arrow-go/v18/arrow/memory"
)
func main() {
mem := memory.NewGoAllocator()
st := arrow.StructOf(
arrow.Field{Name: "city", Type: arrow.BinaryTypes.String},
arrow.Field{Name: "pop", Type: arrow.PrimitiveTypes.Int64},
)
sb := array.NewStructBuilder(mem, st)
defer sb.Release()
sb.Append(true)
sb.FieldBuilder(0).(*array.StringBuilder).Append("Tokyo")
sb.FieldBuilder(1).(*array.Int64Builder).Append(14000000)
sb.Append(true)
sb.FieldBuilder(0).(*array.StringBuilder).Append("Paris")
sb.FieldBuilder(1).(*array.Int64Builder).Append(2200000)
arr := sb.NewArray()
defer arr.Release()
rows, err := arreflect.ToAnySlice(arr)
if err != nil {
panic(err)
}
for _, row := range rows {
fmt.Println(row)
}
}
Output: {Tokyo 14000000} {Paris 2200000}
Example (NullableFields) ¶
package main
import (
"fmt"
"reflect"
"github.com/apache/arrow-go/v18/arrow"
"github.com/apache/arrow-go/v18/arrow/array"
"github.com/apache/arrow-go/v18/arrow/array/arreflect"
"github.com/apache/arrow-go/v18/arrow/memory"
)
func main() {
mem := memory.NewGoAllocator()
st := arrow.StructOf(
arrow.Field{Name: "name", Type: arrow.BinaryTypes.String, Nullable: false},
arrow.Field{Name: "score", Type: arrow.PrimitiveTypes.Float64, Nullable: true},
)
sb := array.NewStructBuilder(mem, st)
defer sb.Release()
sb.Append(true)
sb.FieldBuilder(0).(*array.StringBuilder).Append("alice")
sb.FieldBuilder(1).(*array.Float64Builder).Append(9.5)
sb.Append(true)
sb.FieldBuilder(0).(*array.StringBuilder).Append("bob")
sb.FieldBuilder(1).(*array.Float64Builder).AppendNull()
arr := sb.NewArray()
defer arr.Release()
rows, err := arreflect.ToAnySlice(arr)
if err != nil {
panic(err)
}
for _, row := range rows {
v := reflect.ValueOf(row)
var name string
var scoreField reflect.Value
for i := 0; i < v.NumField(); i++ {
switch v.Type().Field(i).Tag.Get("arrow") {
case "name":
name = v.Field(i).String()
case "score":
scoreField = v.Field(i)
}
}
if scoreField.IsNil() {
fmt.Printf("%s: <null>\n", name)
} else {
fmt.Printf("%s: %.1f\n", name, scoreField.Elem().Float())
}
}
}
Output: alice: 9.5 bob: <null>
func ToSlice ¶
Example ¶
package main
import (
"fmt"
"github.com/apache/arrow-go/v18/arrow/array"
"github.com/apache/arrow-go/v18/arrow/array/arreflect"
"github.com/apache/arrow-go/v18/arrow/memory"
)
func main() {
mem := memory.NewGoAllocator()
b := array.NewFloat64Builder(mem)
defer b.Release()
b.Append(1.1)
b.Append(2.2)
b.Append(3.3)
arr := b.NewArray()
defer arr.Release()
vals, err := arreflect.ToSlice[float64](arr)
if err != nil {
panic(err)
}
fmt.Println(vals)
}
Output: [1.1 2.2 3.3]
Types ¶
type Option ¶
type Option func(*tagOpts)
Option configures encoding behavior for FromSlice and RecordFromSlice.
func WithDecimal ¶
WithDecimal sets the precision and scale for decimal types.
func WithDict ¶
func WithDict() Option
WithDict requests dictionary encoding for the top-level array.
func WithLarge ¶
func WithLarge() Option
WithLarge requests Large type variants (LARGE_STRING, LARGE_BINARY, LARGE_LIST, LARGE_LIST_VIEW) for the top-level array and recursively for nested types.
Example ¶
package main
import (
"fmt"
"github.com/apache/arrow-go/v18/arrow/array/arreflect"
"github.com/apache/arrow-go/v18/arrow/memory"
)
func main() {
mem := memory.NewGoAllocator()
arr, err := arreflect.FromSlice([]string{"hello", "world"}, mem, arreflect.WithLarge())
if err != nil {
panic(err)
}
defer arr.Release()
fmt.Println("Type:", arr.DataType())
fmt.Println("Len:", arr.Len())
}
Output: Type: large_utf8 Len: 2
func WithTemporal ¶
WithTemporal overrides the Arrow temporal encoding for time.Time slices. Valid values: "date32", "date64", "time32", "time64", "timestamp" (default). Equivalent to tagging a struct field with arrow:",date32" etc. Invalid values cause FromSlice to return an error.
func WithView ¶
func WithView() Option
WithView requests view-type encoding (STRING_VIEW, BINARY_VIEW, LIST_VIEW) for the top-level array and recursively for nested types.
Example ¶
package main
import (
"fmt"
"github.com/apache/arrow-go/v18/arrow/array/arreflect"
"github.com/apache/arrow-go/v18/arrow/memory"
)
func main() {
mem := memory.NewGoAllocator()
arr, err := arreflect.FromSlice([]string{"hello", "world"}, mem, arreflect.WithView())
if err != nil {
panic(err)
}
defer arr.Release()
fmt.Println("Type:", arr.DataType())
fmt.Println("Len:", arr.Len())
}
Output: Type: string_view Len: 2