Skip to main content

Parquet

Apache Parquet is a column-oriented binary storage format optimized for analytical workloads. Originally developed within the Apache Hadoop ecosystem, Parquet provides efficient compression and encoding schemes for large-scale data processing.

Binary Layout

SectionInternal NameDescriptionPossible Values / Format
File Headermagic4-byte magic number identifying Parquet filesASCII: PAR1 (hex: 50 41 52 31)
Row Grouprow_group_metadataMetadata for each row groupContains column chunk metadata and statistics
column_chunkData for each column in the row groupCompressed and encoded column data
File FootermetadataFile-level metadata including schema and row groupsThrift-encoded metadata structure
metadata_lengthLength of metadata section4-byte little-endian integer
magicFooter magic numberASCII: PAR1 (hex: 50 41 52 31)

Column Storage Example

Row-based storage (traditional):

id,name,last_name,age
1,John,Buck,35
2,Jane,Doe,27
3,Joe,Dane,42

Column-based Storage (Parquet):

id: [1, 2, 3]
name: [John, Jane, Joe]
last_name: [Buck, Doe, Dane]
age: [35, 27, 42]

JSON Schema Definition

A Parquet schema is declared in VirtualMetric as a single JSON object whose keys are field names and whose values are field definitions. Definitions nest via the fields property to describe records and lists of records. The schema is parsed into a Parquet schema tree and used by the writer when producing .parquet files.

{
"user_id": { "type": "STRING", "requirement": "required" },
"score": { "type": "INT", "bitWidth": 32, "signed": true },
"metadata": {
"type": "GROUP",
"fields": {
"region": { "type": "STRING" },
"tier": { "type": "STRING" }
}
}
}

The top level is always an object — there is no wrapping fields, name, or schema key.

Field Definition Properties

PropertyTypeApplies toDescription
typestringevery fieldLogical type (see below); required for every leaf field
fieldsobjectgroups, complex listsNested fields; presence promotes the field to a record/group
requirementstringevery fieldrequired, optional, or recommended. Default: optional
repeatedbooleanevery fieldIf true, the field is repeated (array-like); equivalent to type: LIST
optionalbooleanevery fieldMarks the field nullable in the internal mapping; does not change repetition
bitWidthintegerINTInteger bit width: 8, 16, 32, or 64. Required when type is INT
signedbooleanINTtrue for signed, false for unsigned. Default: unsigned
logicalTypestringINT64TIMESTAMP_NANOS, TIMESTAMP_MICROS, or TIMESTAMP_MILLIS to mark a timestamp
unitstringINT64 ts, TIMETime unit NANOS, MICROS, or MILLIS; inferred from logicalType if unset
precisionintegerDECIMALTotal number of digits
scaleintegerDECIMALNumber of digits after the decimal point
typeLengthintegerFIXED_LEN_BYTE_ARRAYLength in bytes; must be greater than zero
compressionstringevery fieldAccepted but not used at parse time (see Compression Codecs)
adjustedToUtcbooleantimestamp fieldsAccepted but not used; reserved for future use

Any property not in this table is ignored on parse.

Repetition is decided in order: if type is LIST or repeated is true, the field is repeated; otherwise if requirement is required, the field is required; otherwise it is optional (the default, covering optional, recommended, and unset).

Supported Types

typeNotes
STRINGUTF-8 string
BOOLEANBoolean
INTGeneric integer; requires bitWidth; honors signed
INT32Fixed 32-bit signed integer
INT64Fixed 64-bit signed integer; with logicalType TIMESTAMP_* becomes a timestamp
INT96Legacy 96-bit integer (decoded as INT64 internally)
FLOAT32-bit floating point
DOUBLE64-bit floating point
BYTE_ARRAYVariable-length binary
FIXED_LEN_BYTE_ARRAYFixed-length binary; requires typeLength greater than zero
DECIMALFixed-point decimal; requires precision, scale recommended
DATECalendar date (no time)
DATETIMETimestamp at microsecond precision
TIMETime of day; honors unit, defaults to MILLIS
JSONStored as a UTF-8 string, logically tagged as JSON
GROUPRecord/struct; use with fields to declare members
LISTRepeated field; see Lists

For DECIMAL, the storage backing is chosen automatically from precision: up to 9 digits uses INT32, up to 18 uses INT64, and beyond 18 uses FIXED_LEN_BYTE_ARRAY.

Lists

A list takes one of three shapes:

{
"tags": { "type": "LIST" },
"scores": {
"type": "LIST",
"fields": { "element": { "type": "INT", "bitWidth": 32, "signed": true } }
},
"events": {
"type": "LIST",
"fields": {
"ts": { "type": "INT64", "logicalType": "TIMESTAMP_MILLIS" },
"name": { "type": "STRING" }
}
}
}

tags is the shorthand form (a list of strings); scores names a single element type; events is a list of records. Writing "repeated": true on any scalar produces a repeated field without type: LIST.

Requirement Filtering

The Enforce Schema processor's requirement_filter field selects which fields a schema emits: an empty value or all includes every field, while a comma-separated list of required, optional, and recommended includes only matching fields. This lets one schema produce either a required-only projection or a full projection without rewriting it.

Reference Example

{
"id": { "type": "STRING", "requirement": "required" },
"received_at": { "type": "INT64", "logicalType": "TIMESTAMP_MICROS", "requirement": "required" },
"severity": { "type": "INT", "bitWidth": 8, "signed": false },
"score": { "type": "DECIMAL", "precision": 12, "scale": 4 },
"tags": { "type": "LIST" },
"payload": { "type": "JSON" },
"user": {
"type": "GROUP",
"fields": {
"id": { "type": "STRING", "requirement": "required" },
"email": { "type": "STRING" }
}
}
}

Field mapping uses dot notation (user.id, user.email). Schemas in this format can be registered as reusable Library entries and referenced by name from the Enforce Schema and Check Schema processors, or supplied inline.

Encoding Types

EncodingInternal NameDescriptionUse Case
PlainPLAINValues encoded back to back without compressionDefault fallback for all data types
DictionaryRLE_DICTIONARYValues replaced with dictionary indices using RLERepeated string values, low-cardinality columns
Run Length / Bit-Packing HybridRLECombination of bit-packing and run length encodingRepetition/definition levels, dictionary indices, booleans
Delta Binary PackedDELTA_BINARY_PACKEDDelta encoding with binary packing for integersINT32, INT64 with sequential or clustered values
Delta Length Byte ArrayDELTA_LENGTH_BYTE_ARRAYDelta-encoded lengths followed by concatenated dataVariable-length byte arrays
Delta Byte ArrayDELTA_BYTE_ARRAYIncremental/front compression storing prefix lengthsBYTE_ARRAY, FIXED_LEN_BYTE_ARRAY with common prefixes
Byte Stream SplitBYTE_STREAM_SPLITScatters bytes to separate streams for better compressionFLOAT, DOUBLE, INT32, INT64 (added in Parquet 2.8)

Deprecated Encodings

EncodingInternal NameDescriptionReplacement
Plain DictionaryPLAIN_DICTIONARYLegacy dictionary encoding in data pagesUse RLE_DICTIONARY in data pages
Bit PackedBIT_PACKEDFixed-width bit-packing without paddingUse RLE hybrid encoding

Compression Codecs

CodecDescriptionBest For
UNCOMPRESSEDNo compression appliedTesting or very small files
SNAPPYFast compression/decompressionGeneral-purpose, balanced performance
GZIPHigher compression ratioStorage-constrained environments
LZOFast decompressionRead-heavy workloads
BROTLIModern compression algorithmHigh compression ratio needs
LZ4Extremely fast compressionLow-latency applications
ZSTDZstandard compression with configurable levelsBest balance of speed and compression ratio