Skip to content
Open
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 59 additions & 1 deletion site/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ The supported types are:
* `date`: calendar dates, written as ISO 8601 strings (`YYYY-MM-DD`, e.g. `2024-01-31`).
* `datetime`: date-times, written as ISO 8601 strings. Without a `time_zone` they carry an offset (e.g. `2024-01-31T09:30:00Z`); with a `time_zone` they're written zoneless and interpreted in that zone (see [Time zones](#time-zones)).
* `enum`: a column with repeated values from a known set. The allowed values are listed in the `values` property.
* `list(element_type)`: an ordered sequence of zero or more elements of the given type (see [List element types](#list-element-types)).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are list and struct the right names?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

list definitely. struct is as good as any imo

* `struct`: a structured record with named fields documented in the required `fields` property (see [Struct fields](#struct-fields)).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you thinking of using these for very nested data? I personally haven't encountered these "in the wild". What would be the advantage of a list(struct) rather than as either another table or as a flattened set of columns with specific items appended to their column name.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not a data structure that I'd generally recommend, since I would prefer a relation framing with multiple tables, but it is something that you get in parquet files so its important that we can represent it.


#### Measures

Expand All @@ -156,9 +158,63 @@ A `number(quantity)` column can also declare its `units`: a free-text string nam
range: [0, 5000]
```

#### List element types

The element type in `list(element_type)` may be any type: `string`, `number`, `number(id)`, `number(ordinal)`, `number(quantity)`, `boolean`, `date`, `datetime`, `enum`, or `struct`. The same properties that apply to a column of that type apply when it is used as a list element type — `values` for `enum`, `fields` for `struct`, and so on.

```yaml
- name: tags
type: list(string)
examples: [nature, outdoor, urban, photography, wildlife]
Comment on lines +166 to +167

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment below about the enum. How would this be different?


- name: categories
type: list(enum)
values: [food, drink, dessert]
Comment on lines +170 to +171

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks the same as an enum. What's different here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a multiselect — an enum must have exactly one value for each row. A list(enum) can have any number of values for each row.


- name: line_items
type: list(struct)
fields:
- name: product_id
type: number(id)
examples: [101, 204, 389]
- name: quantity
type: number(quantity)
units: units
range: [1, 100]
- name: price
type: number(quantity)
units: USD
range: [0.99, 999.99]
```

#### Struct fields

A `struct` column may include a `fields` property — an ordered list of field descriptors. Each field descriptor uses the same schema as a column descriptor, with two differences:

* `primary_key` and `foreign_key` constraints are not meaningful on struct fields and are not permitted.
* A field may itself be `list(...)` or `struct` (with its own `fields`), allowing deep nesting.

```yaml
- name: address
type: struct
fields:
- name: street
type: string
examples: [123 Main St, 456 Oak Ave, 789 Elm Dr]
- name: city
type: string
examples: [Portland, Austin, Chicago]
- name: zip
type: string
examples: ["97201", "78701", "60601"]
- name: country
type: enum
values: [US, CA, MX]
Comment on lines +194 to +208

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a hard time seeing the difference here with the list(struct). I understand that the data itself might be different, but when the metadata looks the exact same, I'd have a hard time differentiating them both.

```

#### Representative values

Every type has some way of representing the data it contains: an exhaustive set of values, a range, or a handful of examples. Each such column carries exactly one of the following three properties, determined by the column's `type`:
Most typed columns carry exactly one of the following three properties to represent the data they contain. The exceptions are `boolean` (values are always `true`/`false`) and `struct` (whose fields carry their own).

* `values`: the allowed values for an `enum` column. Can be a list (`[M, F, U]`) when values are self-explanatory, or a map (`{M: Male, F: Female, U: Unknown}`) when values need labels. The values themselves must be scalars (string, number, or boolean); in the map form the labels must be strings. (`boolean` columns implicitly have `values: [true, false]`, no need to explicitly include it.)
* `range`: a two-element list `[min, max]` giving the inclusive minimum and maximum *observed* in the column. Like `examples`, it describes the data rather than constraining it — a value outside the range will generate a warning, not a validation error. Used for the ordered numeric and temporal types: `number(ordinal)`, `number(quantity)`, `date`, and `datetime`. Both elements must match the column's type, and the minimum must not exceed the maximum.
Expand All @@ -168,6 +224,8 @@ Every type has some way of representing the data it contains: an exhaustive set

`boolean` columns are the exception to this rule because they can only contain `true`, `false`, and (if not required) `null`.

For `list(element_type)` columns, the same three properties apply but describe the element values, not the lists themselves. Use `values` for `list(enum)`, `range` for `list(number(ordinal))`, `list(number(quantity))`, `list(date)`, and `list(datetime)`, and `examples` for all other list types. Each property means the same thing it would for a scalar column of the element type — for instance, `range` on a `list(number(quantity))` column gives the minimum and maximum element value observed across all lists.

#### Time zones

A `datetime` column can declare its `time_zone`, which says how to interpret its values as moments in time. The value is either an [IANA time zone name](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) or the sentinel `naive`:
Expand Down
Loading