Python SDK

Relationships

Link Python datasets and query to-one related fields one hop deep.

Relationships model how one dataset links to another. To-one relationships (belongs_to, has_one) are queryable: dataset queries can select, filter, and order by fields on the related dataset one hop deep, and hypequery executes the join for you. To-many relationships (has_many) are metadata only.

Defining relationships

Pass the target dataset (or a function returning it). from_field is the join column on this dataset's table and to_field is the join column on the target's table.

from hypequery.datasets import (
    Dataset,
    belongs_to,
    create_dataset_client,
    create_dataset_registry,
    dimension,
    has_many,
    measure,
)

customers = Dataset(
    name="customers",
    source="customers",
    dimensions={
        "id": dimension("string"),
        "country": dimension("string"),
        "tier": dimension("string"),
    },
)

line_items = Dataset(
    name="line_items",
    source="line_items",
    dimensions={"id": dimension("string"), "sku": dimension("string")},
)

orders = Dataset(
    name="orders",
    source="orders",
    dimensions={
        "id": dimension("string"),
        "status": dimension("string"),
        "amount": dimension("number"),
    },
    measures={"revenue": measure.sum("amount")},
    relationships={
        "customer": belongs_to(customers, from_field="customer_id", to_field="id"),
        "items": has_many(line_items, from_field="id", to_field="order_id"),
    },
)

# The registry resolves relationship targets at query time.
analytics = create_dataset_client(
    executor=executor,
    registry=create_dataset_registry(orders, customers, line_items),
)
  • belongs_to: many-to-one; the foreign key is on this table (orders.customer_id → customers.id).
  • has_one: one-to-one; the foreign key is on the target table.
  • has_many: one-to-many; the foreign key is on the target table. Metadata only.

Reference to-one related dimensions as <relationship>.<dimension> in dimensions, filters, and ordering:

from hypequery.datasets import DatasetQuery, filter, order

result = analytics.execute(
    "orders",
    DatasetQuery(
        dimensions=("customer.country",),
        measures=("revenue",),
        filters=(filter.eq("customer.tier", "enterprise"),),
        order_by=(order.asc("customer.country"),),
    ),
)

A target can be a Dataset or the name of a registered dataset.

Served endpoints don't expose related fields unless you opt in per relationship with include_related. See Dataset endpoints.

Checking join keys

check_relationships(dataset, *, executor, registry, context=None, relationships=None) runs scoped uniqueness checks on the physical target keys of each to-one relationship and reports duplicates, so a belongs_to that would fan out rows is caught before it reaches a dashboard. check_relationships_async is the async form. Both respect the tenant scope, deadline, and cancellation in context, and neither runs unless you call it.

On this page

Get early access to hypequery cloud.

Cloud hosts the same model you run in your backend, with a managed API and MCP server, versions, and rollback. We're letting teams in a few at a time. Join the waitlist and we'll reach out when your project can go live.

How do you want to build your analytics layer? (Optional)
What features do you most need? (Optional)

Pick as many as you like.

Only your email is required. All other questions are optional.