📊

SPEC-STV-05-Databases

📜

SPEC-STV-05 · Spec header. Spec ID: SPEC-STV-05 · Title: Databases / Tables System · Version: 1.0.0 · Status: Planned · Authority: Specification · Priority: P1 · Owner role: Backend architect · Reviewers: Frontend lead, Database architect · Last reviewed: 2026-05-11 · Sync targets: app/Services/Databases/**, docs/DATABASE_SCHEMA.md (db tables section) · Depends on: SPEC-STV-HUB, SPEC-STV-02, SPEC-STV-04 · Consumed by: SPEC-STV-03, SPEC-STV-09 · Conflict rule: Hub wins. · Change policy: Backend architect + Frontend lead; Registry bump.

1 · Concept

A documentation database is a structured collection of rows hosted on a page. It is conceptually a Notion-style database (not a MySQL database). Each database has:

  • a schema of properties (columns),
  • many rows with cell values keyed by property,
  • many views that render the same rows differently.

Databases live in documentation_databases. They can be embedded into another page via the database_view block.

2 · Property types (15)

typeconfigcell value shape
text{ multiline?: bool }{ rich: RichText[] }
number{ format: "plain|percent|currency", currency?, decimals? }{ n: number }
select{ options: [{ id, label, color }] }{ option_id }
multi_select{ options: [...] }{ option_ids: [] }
date{ include_time: bool, range: bool }{ start: iso, end?: iso, time?: bool }
checkbox{}{ checked: bool }
user{ multi: bool }{ user_uuids: [] }
file{ multi: bool }{ file_uuids: [] }
url{}{ url: string }
email{}{ email: string }
phone{}{ phone: string }
status{ groups: [{ name, options: [...] }] }{ option_id }
relation{ target_database_id, multi: bool }{ row_ids: [] }
formula{ expression: string }{ result: number|string|bool|date } (computed)
rollup{ relation_property_id, target_property_id, aggregation: "sum|count|min|max|avg|earliest|latest|any|every" }{ result } (computed)

relation, formula, rollup ship in P2.1 (after the v1 set is stable).

3 · Denormalization for search/filter

database_cell_values carries value_text, value_number, value_date populated by DatabaseService::writeCells(). Filters and sorts in views use these columns (with indexes) instead of JSON path queries.

4 · View types (6)

typeconfig keysNotes
table{ visible_properties: [], frozen_property_id?, sort: [...], filter: ..., group?: property_id }Default.
board{ group_by: property_id (select|status), card_properties: [...] }Kanban.
list{ title_property_id, subtitle_properties: [...] }Compact.
gallery{ cover_property_id (file|image), card_properties: [...] }Image cards.
calendar{ date_property_id, end_date_property_id? }Month/week view.
timeline{ start_property_id, end_property_id, scale: "day|week|month|quarter" }P5+.

5 · Filter / sort / group spec

Filter expression:

{
  "op": "and",
  "conditions": [
    { "property_id": 12, "op": "equals", "value": "opt_open" },
    { "op": "or", "conditions": [
      { "property_id": 9, "op": "contains", "value": "docs" },
      { "property_id": 9, "op": "is_empty" }
    ]}
  ]
}

Sort: [{ property_id, direction: "asc|desc" }]. Group: a single property_id.

All evaluated server-side using the denormalized columns; never read JSON for filter.

6 · Inline as block

The database_view block embeds a database. embed: "inline" renders the rows inline on the page; embed: "linked" renders a link card. Permissions on the host page do not widen access to the database — the database's own visibility wins.

7 · API

See SPEC-STV-03 §7. All filter/sort/group config is server-applied; the client must not paginate without honoring the view config.

8 · Migration / refactor rules

  • A property type change requires a migration job that rewrites cell values + denorm columns. Disallowed if data would be lost; offer dry-run.
  • Deleting a property deletes all cell values for it (cascade).
  • A relation property delete also clears rollups that consume it.