Docs

Projections

A projection turns dataset rows into ontology objects, links, and telemetry points. Reach for one when you have clean tables — say invoices and customers pulled from your ERP — and want them to become the Invoice and Customer objects your app reads through sixb.objects(...).

Syncs and pipelines prepare the rows; projections create the objects and links on top of them. There are three kinds:

BuilderProducesOne row becomes
defineProjectionObjects (and FK links)One object upsert
defineLinkProjectionMany-to-many linksOne link
defineTelemetryProjectionTelemetry pointsOne reading on a series

Object projection

Each row becomes one object. Map object properties to dataset columns with .properties(...). The primary property (usually id) must be mapped.

TS
import { defineProjection } from "@sixb/core"
import { erpCustomersDataset } from "../datasets/erp"
import { Customer } from "../ontology/customer"

export const customerProjection = defineProjection("customer-proj", Customer)
  .fromDataset(erpCustomersDataset)
  .properties({
    id: "customer_id",
    name: "contact_name",
    email: "contact_email",
    company: "company_name",
    industry: "industry_sector",
    tier: "service_tier",
  })
PartMeaning
defineProjection(id, ObjectType)Names the projection and its target object type
.fromDataset(dataset)Chooses the source dataset
.properties({ prop: "column" })Maps object property ids to dataset column names

The object property id reads from column customer_id, name from contact_name, and so on.

When a row carries a foreign key, turn it into an ontology link with .withLinks(...). Each entry is keyed by the link id and uses the inline { link, sourceField, target } descriptor:

TS
import { defineProjection } from "@sixb/core"
import { erpInvoicesDataset } from "../datasets/erp"
import { Customer } from "../ontology/customer"
import { Invoice } from "../ontology/invoice"
import { Project } from "../ontology/project"

export const invoiceProjection = defineProjection("invoice-proj", Invoice)
  .fromDataset(erpInvoicesDataset)
  .properties({
    id: "id",
    number: "number",
    amount: "amount",
    currency: "currency",
    status: "status",
    issuedAt: "issuedAt",
    dueDate: "dueDate",
  })
  .withLinks({
    customer: {
      link: Invoice.l.customer,
      sourceField: "customer_id",
      target: Customer,
    },
    project: {
      link: Invoice.l.project,
      sourceField: "project_id",
      target: Project,
    },
  })

The value in customer_id equals the primary id of a Customer, so the projection creates the Invoice -> Customer link; project_id creates Invoice -> Project.

Descriptor fields

FieldMeaning
linkThe link token from the source object type (SourceType.l.<linkId>)
sourceFieldDataset column holding the target's primary id
sourcePropertyAlternative to sourceField: a projected property token (SourceType.p.<propId>) holding the target id
targetThe target object type (must be the link's declared target or a subtype via extends)

sourceField and sourceProperty are mutually exclusive — provide exactly one. Use sourceField when the foreign key lives only in the dataset; use sourceProperty when you also map that column to an object property and want to reuse it.

The inline descriptor is sugar over the fromForeignKey() helper. The two are equivalent — prefer the inline form; reach for fromForeignKey() only to build a descriptor separately.

When a join dataset stores relationships, use defineLinkProjection. Each row becomes one link from a source object to a target object, identified by their primary ids.

TS
import { defineLinkProjection } from "@sixb/core"
import { erpProjectMembersDataset } from "../datasets/erp"
import { Project } from "../ontology/project"

export const projectMembersProjection = defineLinkProjection("project-members", Project.l.members)
  .fromDataset(erpProjectMembersDataset)
  .sourceField("project_id")
  .targetField("employee_id")
PartMeaning
defineLinkProjection(id, SourceType.l.<linkId>)Names the projection and its target link
.sourceField("column")Dataset column holding the source object's primary id
.targetField("column")Dataset column holding the target object's primary id

Source and target fields must be string columns.

Telemetry projection

A telemetry projection records timestamped readings onto a telemetry-mode property. Use it when a dataset has one row per measurement: a value, the object it belongs to, and when it was recorded.

First mark the property as telemetry in the ontology (see Properties):

TS
prop("progress", "integer", { mode: "telemetry" })

Then map a dataset of readings onto it with .points(...):

TS
import { defineTelemetryProjection } from "@sixb/core"
import { erpProjectProgressDataset } from "../datasets/erp"
import { Project } from "../ontology/project"

export const projectProgressProjection = defineTelemetryProjection(
  "project-progress",
  Project.p.progress
)
  .fromDataset(erpProjectProgressDataset)
  .points({
    objectId: "project_id",
    at: "recorded_at",
    value: "progress_pct",
  })
Mapping keyMeaning
objectIdDataset column holding the target object's primary id
atTimestamp column for the reading
valueColumn holding the reading
unitOptional column holding the reading's unit (required only for properties that carry a unit)

Each row appends one point to the progress series of the Project named by project_id. The at column must be a string, date, or timestamp; values without a time zone (no trailing Z or numeric offset) are read as UTC.

How point identity works — and what re-projecting the same instant does — is covered in Telemetry.

Projection vs pipeline

Pipelines and projections solve different problems: pipelines make better rows, projections make objects.

NeedUse
Clean, filter, join, or reshape rowsPipeline
Create app objects from rowsProjection
Create object relationships from foreign keysProjection
Record timestamped readings on a propertyProjection
Keep data as tablesDataset or pipeline

Registration

Put projection definitions in projections/ and export them. createSixb() discovers them automatically.

TXT
your-project/
  datasets/
    erp.ts
  ontology/
    customer.ts
    invoice.ts
  projections/
    customer-projection.ts
    invoice-projection.ts
  sixb.config.ts

You can also register them explicitly:

TS
import { createSixb } from "@sixb/core"
import { erpInvoicesDataset } from "./datasets/erp"
import { Invoice } from "./ontology/invoice"
import { invoiceProjection } from "./projections/invoice-projection"

export const sixb = await createSixb({
  ontologies: [Invoice],
  datasets: [erpInvoicesDataset],
  projections: [invoiceProjection],
})

See the Runtime overview for how discovery works.

Running projections

A committed dataset version triggers projection execution. In local development, sixb dev co-hosts projection workers when projections are registered. For a separate worker process:

BASH
sixb worker projection

Behavior and validation

  • .properties(...) checks that mapped properties and columns exist and that their types are compatible. The primary property must be mapped.
  • Projections are set-only: missing rows do not delete existing objects or links.
  • Link projections require string source and target fields.
  • For an FK descriptor, target must be the link's declared target type or a subtype (via extends).

Search docs

Search the documentation