A dataset is a named, environment-aware pointer to data in
Azure Storage. You declare where the data lives once,
and then ask azr to build the concrete URI for whichever
environment (tier) you are running in. A catalog groups related
datasets so they can be stored, shared, and resolved together.
This separates two concerns that usually get tangled together in pipeline code:
- the stable identity of a dataset (its name, container, path, format), and
- the environment-specific storage account it currently lives in.
A single dataset
azr_dataset() declares one dataset. Only the storage
account varies by tier — the container, path, scheme, and format are
shared across every environment:
orders <- azr_dataset(
name = "orders",
scheme = "abfss",
container = "raw",
storage = list(prod = "stprod001", preprod = "stpreprod001"),
path = "sales/orders",
format = "delta"
)
ordersname must match ^[a-z][a-z0-9_]*$ so it can
be used as a stable lookup key, path must be non-empty (a
dataset cannot point at a container root), and format must
be one of delta, parquet, csv,
tsv, json, avro,
orc, or text.
Building a URI
azr_dataset_uri() turns a dataset into a concrete URI
for one tier. The default uri_type = "hadoop" produces the
ABFS form used by Spark, Flink, Trino, and any hadoop-azure
consumer:
azr_dataset_uri(orders, tier = "prod")
#> "abfss://raw@stprod001.dfs.core.windows.net/sales/orders"Switch tier to point the same dataset at a different storage account,
or ask for the https form:
azr_dataset_uri(orders, tier = "preprod", uri_type = "https")
#> "https://stpreprod001.dfs.core.windows.net/raw/sales/orders"Requesting a tier the dataset doesn’t declare is an error that lists the tiers it does have, so misconfiguration fails loudly rather than silently reading the wrong environment.
The default tier
tier defaults to the dataset_tier option,
so production code rarely needs to pass it. Set it once via
options(azr.dataset_tier = ...) or the
AZR_DATASET_TIER environment variable (default
"prod") — for example, a pre-production job can export
AZR_DATASET_TIER=preprod and every
azr_dataset_uri() call follows.
Resolving to a manifest
A URI alone isn’t enough to load data — a reader also needs
the format. azr_resolve_dataset() returns an
azr_dataset_manifest carrying the name,
uri, and format together (call
as.list() for a plain R list):
m <- azr_resolve_dataset(orders, tier = "prod")
m
#> <azr_dataset_manifest:orders>
#> uri: abfss://raw@stprod001.dfs.core.windows.net/sales/orders
#> format: deltaThis is exactly the shape a reader like
sparklyr::spark_read_source() wants:
m <- azr_resolve_dataset(orders, tier = "prod")
spark_read_source(sc, name = m@name, source = m@format, path = m@uri)Creating a dataset from a URI
If you already have a full storage URI,
azr_dataset_from_uri() parses it into a dataset. The parsed
storage account is bound to tier; name
defaults to the last path segment (with any file extension removed); and
format is inferred from the URI’s file extension (or
_delta_log segment) when you don’t pass it:
file <- azr_dataset_from_uri(
"abfss://raw@stprod001.dfs.core.windows.net/sales/orders/part.parquet",
name = "orders_file"
)
file
#> <azr_dataset:orders_file>
#> scheme: abfss
#> container: raw
#> path: sales/orders/part.parquet
#> format: parquet
#> storage: prod=stprod001A directory URI has no file extension to infer from, so
format defaults to "delta". Pass
format explicitly to override it (for example a directory
of Parquet files):
# Directory URI → format defaults to "delta"
azr_dataset_from_uri(
"abfss://raw@stprod001.dfs.core.windows.net/sales/orders",
name = "orders"
)
# Override the default for a non-delta directory
azr_dataset_from_uri(
"abfss://raw@stprod001.dfs.core.windows.net/sales/orders",
name = "orders",
format = "parquet"
)To make the dataset multi-tier, supply additional accounts via
storage:
azr_dataset_from_uri(
"abfss://raw@stprod001.dfs.core.windows.net/sales/orders",
name = "orders",
format = "delta",
tier = "prod",
storage = list(preprod = "stpreprod001")
)Grouping datasets in a catalog
An azr_catalog() holds an ordered collection of datasets
with unique names. It behaves like a named container: index with
[[, and use names() and
length():
catalog <- azr_catalog(datasets = list(orders, file))
names(catalog)
#> "orders" "orders_file"
length(catalog)
#> 2
catalog[["orders"]]azr_dataset_uri() and azr_resolve_dataset()
both dispatch on a catalog. With no name, they return one
entry per dataset, keyed by name:
azr_dataset_uri(catalog, tier = "prod")
#> orders "abfss://raw@stprod001.dfs.core.windows.net/sales/orders"
#> orders_file "abfss://raw@stprod001.dfs.core.windows.net/sales/orders/part.parquet"Pass name to resolve a single dataset out of the
catalog:
azr_dataset_uri(catalog, tier = "prod", name = "orders")
azr_resolve_dataset(catalog, tier = "prod", name = "orders")Storing a catalog as JSON
A catalog round-trips to JSON, so you can keep dataset definitions in
version control and load them at runtime.
azr_catalog_write() serialises a catalog and
azr_catalog_read() reads it back:
azr_catalog_write(catalog, "datasets.json")
catalog <- azr_catalog_read("datasets.json")The JSON shape is one object per dataset under a top-level
datasets array:
{
"datasets": [
{
"name": "orders",
"scheme": "abfss",
"container": "raw",
"storage": { "prod": "stprod001", "preprod": "stpreprod001" },
"path": "sales/orders",
"format": "delta"
}
]
}azr_catalog_read() validates every entry, reporting
which dataset (by index and name) is invalid and why — so a malformed
catalog fails at load time rather than when a pipeline later tries to
read a dataset.
Putting it together
A typical flow: keep a datasets.json in the repo, read
it into a catalog, and resolve the dataset you need for the current
tier.
catalog <- azr_catalog_read("datasets.json")
m <- azr_resolve_dataset(catalog, name = "orders") # tier from AZR_DATASET_TIER
df <- spark_read_source(sc, name = m@name, source = m@format, path = m@uri)The same code runs unchanged in every environment; only the
dataset_tier option (or AZR_DATASET_TIER)
decides which storage account the URIs point at.
