Properties

A property is a named value on an object, such as an invoice's number, status, or due date. Its schema determines which values Sixb accepts.

Define a property

Add prop(id, schema, options?) to an object type's properties array:

TS
import { prop, stringEnum } from "@sixb/core/ontology"

prop("number", "string", { required: true })
prop("status", stringEnum(["draft", "sent", "paid"]))

IDs must be unique within the type. Use name for a separate display label and description for additional context. Without a name, Sixb uses the ID.

Choose a schema

SchemaValues
"string", "uuid"Text or a UUID string.
"boolean"true or false.
"integer", "double"Whole numbers or floating-point numbers.
"decimal"Exact decimal values, represented as strings.
"date", "timestamp"A calendar date or timestamp. Writes accept Date values or ISO strings.
ref.file()A reference to an uploaded file. The string "fileRef" is equivalent.
ref.user()A reference to a Sixb user, such as an assignee.
stringEnum([...]), integerEnum([...])A fixed set of string or integer values.
array, object, mapStructured values, shown below.
valueTypeRef(ValueType)A reusable schema.

Scalar schemas are strings. Schemas that point somewhere else are built with ref: ref.file(), ref.user(), and ref(ObjectType) for action parameters. To point from one object to another, use a link instead; ref(ObjectType) on a property is rejected.

For exact decimal values, use decimal() with a string rather than converting through a JavaScript number:

TS
import { decimal, prop } from "@sixb/core/ontology"

prop("amount", "decimal")
const amount = decimal("124.50")

Keep a separate currency property when an amount represents money. Physical units are for measurements such as temperature or pressure.

Required and nullable values

Properties are optional and non-nullable by default. required means a new object must include the property. nullable allows an explicit null value. The two options are independent:

TS
prop("number", "string", { required: true })
prop("note", "string", { nullable: true })

An omitted property and a property set to null are different values. Updating an existing object can leave required properties unchanged; see Create or update an object.

The primary property is always a required string.

Structured values

Use arrays for lists, objects for named fields, and maps for dynamic keys:

TS
prop("tags", { type: "array", items: "string" })

prop("address", {
  type: "object",
  properties: {
    street: { schema: "string", required: true },
    city: { schema: "string", required: true },
    postcode: { schema: "string" },
  },
})

prop("labels", { type: "map", keySchema: "string", valueSchema: "string" })

Nested fields can be required or nullable, and structured schemas can be nested. There is no unrestricted json property schema; describe the shape you expect.

Use a value type when several properties share a shape. If the value needs its own identity or relationships, define an object type and connect it with a link.

Reference a user

Use ref.user() when a property names a member of your project, such as a task's assignee or a request's reviewer:

TS
import { prop, ref } from "@sixb/core/ontology"

prop("assignee", ref.user(), {
  nullable: true,
  query: { searchable: true, filterable: true, facet: true },
})
prop("reviewers", { type: "array", items: ref.user() })

The value is { type: "user", id }, with the ID of a Sixb user. A write can only set a user who exists and is active. Values an object already holds stay valid: suspending a user does not block later updates of objects that reference them. The reference carries no display data and grants no access by itself.

Build a value with userRef(id). Queries can match a user with eq, neq, in, and exists, find a user in an array with contains, and group objects by user with facet:

TS
import { userRef } from "@sixb/core"

const assignee = userRef("usr_1")
await sixb.objects(Task).upsert({ properties: { id: "task-1", assignee } })

await sixb
  .objects(Task)
  .query()
  .where((task) => task.or(task.p.assignee.eq(assignee), task.p.reviewers.contains(assignee)))
  .list()

A signed-in Principal whose type is "user" is already a user reference. User references cannot be sorted, searched as text, or listed in search.exact.

User references are set by your code, actions, and workflows. Projections, telemetry properties, link properties, and action parameters of a share cannot use them yet. For a person who is part of your domain rather than a project member, such as a customer contact, define an object type.

Enable queries

Add query options to the properties you filter, sort, search, or group. Set searchable: true alongside the capabilities you need:

TS
prop("status", stringEnum(["draft", "sent", "paid"]), {
  query: { searchable: true, filterable: true, facet: true },
})

prop("dueDate", "date", {
  query: { searchable: true, filterable: true, sortable: true },
})
OptionEnables
searchableRequired for all other query options.
filterableProperty predicates such as equality, ranges, and existence.
sortableOrdering results by this property.
textKeyword search on a string or string enum.
exactUsing this property in the type's exact-match search profile.
facetGrouped counts for scalar, enum, or user reference values, excluding file references.
weightPositive text-ranking weight; also requires text: true and provider support.

Without query options, a property can still be stored and read. Comparing the primary ID with eq or in does not need query options. See Querying for the calls these settings enable.

Set search.defaultText on the object type to choose which fields a plain search("...") query uses. Enable text on those properties in the same definition:

TS
// ontology/customer.ts
import { defineObjectType, prop } from "@sixb/core/ontology"

export const Customer = defineObjectType({
  id: "Customer",
  name: "Customer",
  properties: [
    prop("id", "string", { required: true, primary: true }),
    prop("name", "string", {
      required: true,
      query: { searchable: true, text: true, sortable: true },
    }),
  ],
  search: {
    title: "name",
    defaultText: ["name"],
    exact: ["id"],
  },
})

title selects the string field used to label search results. exact lists exact-match fields; each needs query.exact: true, except the primary ID. Search profiles use static properties.

Use search.vectors to search by meaning. Each named profile combines static string or string-enum properties with a registered embedding model:

TS
// ontology/product.ts
import { defineObjectType, prop } from "@sixb/core/ontology"
import { productEmbedding } from "../lib/models"

export const Product = defineObjectType({
  id: "Product",
  name: "Product",
  properties: [
    prop("id", "string", { required: true, primary: true }),
    prop("title", "string", { required: true }),
    prop("description", "string"),
  ],
  search: {
    vectors: {
      content: { source: ["title", "description"], model: productEmbedding },
    },
  },
})

The source properties need no query flags. Sixb stores embeddings separately from your object's properties. See Search by meaning for indexing and querying.

Record history

Properties use mode: "static" by default: they hold the object's current value. Use mode: "telemetry" when you also need a timestamped history:

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

Telemetry supports scalar and structured values, but cannot contain file or user references. Values with a physical semanticType also require a valid unit.

See Telemetry for appending readings and querying their history.

Protect sensitive values

Mark a property to hide its value from roles without the matching clearance:

TS
prop("amount", "decimal", { markings: [financial] })

See Markings.

Search docs

Search the documentation