Exporting data
See also: Importing data · Component dataframe · Glossary
Take a section's records and turn them into a flat downloadable table (CSV, TSV, ODS, XLSX, HTML or print), or a machine-readable raw CSV for the import tool. This page covers the export tool's UI and, for developers, the export pipeline and component contract. For a complete, verified copy of a section set — the backup, the move between installations, the preservation copy — see The archive door: the export tool is not that.
Introduction
Exporting is the counterpart of importing: it takes the data of a section and turns it into a flat table (rows and columns) that you can download as CSV, TSV, ODS (LibreOffice), XLSX (Excel), HTML, or print. You can also download the media files (images, audiovisuals, PDFs, 3D, SVG) referenced by the exported records.
Because Dédalo stores highly structured data — values in several languages, relations to lists and thesauri, hierarchies, dataframes — exporting is not a simple dump. The export tool lets you decide which components become columns, in which order, and how relations and hierarchies are flattened into a spreadsheet, so the result is meaningful for the use you have in mind (a report, a migration, a backup, an analysis in a spreadsheet, etc.).
A special raw format produces machine-shaped cells ({"dedalo_data": …}) the
CSV import tool recognizes. It is not a backup and not
a way to move data between installations — see
Raw export and the import tool for what it
does and does not preserve, and The archive door for the
door that is.
What gets exported
The export always covers the whole current selection of the section — the set of records produced by your current search/filter — not just the page you are looking at. Configure the search first, then export.
Opening the export tool
- Open a section in list mode and configure the search/filter so the list shows the records you want to export.
- Click the Export button in the section toolbar.
The export tool opens in its own window with three areas:
- Left — the list of the section's components (the available columns). Relation components can be expanded to reach the components of the related section.
- Center — Active elements: the columns you have chosen, in order.
- Right — the configuration panel: presets, format and options, the Export button, the download buttons, and the live preview.
Choosing the columns
Drag a component from the left list and drop it into the Active elements list in the center. Each dropped component becomes one column of the export.
- Order matters. The export columns follow the exact order of the Active elements list. Drag the items up and down to reorder them; the output columns (and every download) follow that order.
- Remove a column with the × on its item.
- Activate all columns / Deactivate all columns add or clear the whole set at once.
- Relations and hierarchies: expand a relation component (▶) on the left to reach the components of the related section and export them as columns too (for example, export the name of a related "Mint" instead of just the link).
The order you drop and drag is the order you get
The list of Active elements is the single source of truth for the column order. If you reorder the items, the next export — and the CSV/Excel/… you download — reflects the new order exactly.
Per-component "parents" (ancestor chain)
Hierarchical components (thesaurus terms) have a chain of ancestors. When you add
such a component you get a small parents checkbox on its item: enable it to add
a sibling column with the term's ancestor chain (joined with >). See
Export parents for the global option.
Export options
All options are in the right-hand configuration panel.
Format
| Format | Value | What it produces |
|---|---|---|
| Standard | value |
One flat value per cell. Multiple values of a relation are joined in the same cell. The most readable format. |
| Breakdown | grid_value |
Relation items are exploded into extra rows and/or extra \|n columns, so each related item gets its own cell. See Breakdown mode. |
| Dédalo (Raw) | dedalo_raw |
Each cell is the stored Dédalo value wrapped as {"dedalo_data": …}. Not meant to be read by humans; it is the format the import tool unwraps (see Raw export and the import tool) — not a verbatim copy of the stored row. |
Breakdown mode
Only applies to the Breakdown format. It controls how a component with several related items (for example a record linked to three "types") is laid out:
| Mode | Value | Layout |
|---|---|---|
| Default | default |
The first relation level becomes extra rows; deeper levels become extra \|n columns. Keeps the legacy behavior. |
| Rows | rows |
Every related item becomes an extra row. Sibling columns are aligned; spanning (parent) values can be repeated down the rows (see Fill the gaps). |
| Columns | columns |
Every related item becomes extra columns with a \|n suffix. One row per record. |
Rows vs Columns
A record R1 linked to mints A and B:
Columns (one row per record):
| id | Mint|1 | Mint|2 |
|---|---|---|
| R1 | A | B |
Rows (one row per related item):
| id | Mint |
|---|---|
| R1 | A |
| R1 | B |
Fill the gaps
(Default on.) In the Rows breakdown, when a record explodes into several rows, the values that belong to the record itself (not to the exploded relation) are repeated on every row instead of being left blank. Turn it off to leave the spanning cells empty except on the first row.
Show ontology tipo
Adds the component ontology tipo to the column headers in the preview (useful to identify exactly which component a column maps to). This only changes the header text shown in the tool.
Export parents
(Default off, not available for the Dédalo raw format.) The global version of
the per-component parents option: for every
relation/hierarchical column, add a sibling column with the ancestor chain of each
linked term (joined with >). You can instead enable it column by column with the
per-item parents checkbox.
The preview
Press Export to run it. A table preview is rendered and fills in live as the records stream from the server, with a progress bar. The preview is what you see is what you get: every download is built from the same data shown in the preview.
The preview has a sticky header, a frozen first (id) column, zebra rows, image and audiovisual thumbnails, and clickable links — so even wide exports stay readable.
Downloading the data
Once the export has run, the download bar offers:
| Button | File | Notes |
|---|---|---|
| CSV | .csv |
;-separated, RFC-4180 quoted. Re-importable (see below). |
| TSV | .tsv |
Tab-separated, unquoted. |
| ODS | .ods |
LibreOffice Calc. |
| XLSX | .xlsx |
Microsoft Excel. |
| HTML | .html |
The table as a standalone HTML page. |
| Media | .zip |
Downloads the media files (image, audiovisual, PDF, 3D, SVG) referenced by the exported records. A dialog lets you pick the quality per media type. |
| — | Opens the browser print dialog for the preview. |
Encoding
Text downloads are UTF-8. CSV uses ; as the field separator and escapes inner
quotes by doubling them, matching the import format.
Saving export configurations (presets)
Building a useful export (the right columns, in the right order, with the right options) takes effort, so you can save it as a preset and reuse it later. Presets are stored per user in the database.
In the presets block at the top of the configuration panel:
- + (New) — saves the current configuration (selected columns + format + breakdown + all options) as a new preset and opens a small editor to give it a name, and optionally mark it Public (shared with all users) or Default.
- Apply (on a preset row) — loads that preset: it rebuilds the selected columns in order and restores the format and options.
- Save changes — updates the currently selected preset with the current configuration.
- Edit / Delete — rename/flag or remove a preset.
Presets are scoped to the section you are exporting (a preset created on one section does not appear on another). Public presets are visible to every user; your own presets are private unless you mark them public.
Presets vs the auto-remembered state
Independently of presets, the tool remembers your last column selection (per section) and your last format/breakdown choice in the browser, so reopening the tool restores where you left off. Presets are the named, shareable, cross-device version stored in the database.
Raw export and the import tool
The Dédalo (Raw) format (dedalo_raw) exports each cell as the stored value,
wrapped with the dedalo_data property:
{"dedalo_data":[{"value":"Hello","lang":"lg-eng","id":1}]}
A CSV produced with this format can be fed to the CSV import tool,
which detects and unwraps the dedalo_data wrapper. What it does not do is
reproduce the stored row verbatim, and the manual used to say it did:
- The import runs the same human-input conform over a wrapped cell as over a
typed one. Measured over every component model:
component_text_arearewrites<br>and newlines into paragraph markup, andcomponent_geolocationwrites the item without its storedid(so the save stamps a fresh one — the identity every remove, Time Machine restore and dataframe pairing addresses). The other models measured lossless. - A component with no stored data exports as an empty cell and imports as an explicit clear — a full-section re-import re-saves every component of every record and stamps a Time Machine row for each.
- Relation cells carry locators —
{section_tipo, section_id}— andsection_idis a per-installation counter value. The import checks a locator's shape only, so in another installation each link resolves to whatever record holds that id there. - Media cells carry
files_info(a manifest of files) and no bytes; the media zip is a separate download with nothing tying the two together.
So use raw export for what it is: a machine-shaped CSV for the same installation, edited or filtered outside Dédalo and brought back through the import tool. For a backup, a move between installations or a preservation copy use The archive door.
See The dedalo_data wrapper and Dataframe columns for the details of the wire shape (including how dataframe rows travel in their own column).
Raw is not for reading
The raw format is meant for machines, not for analysis. Use Standard or Breakdown when a person or a spreadsheet will read the result.
The archive door
The archive is the one complete, self-describing, verified extraction of a
section set, and its reconstruction. It is a command-line door on the server
(bun scripts/archive.ts), not a button in the export tool:
bun scripts/archive.ts extract --sections <tipo,tipo,…> --out <directory>
bun scripts/archive.ts verify --archive <directory>
bun scripts/archive.ts restore --archive <directory> --user-id <n> [--allow-external] …
extract writes a directory (tar it for transport) holding, for every record
of every named section, all eleven stored columns exactly as the database holds
them — nothing conformed, no item renumbered, every locator and every
files_info as stored — plus the ontology subtree that gives them meaning, byte
copies of every media file the records own, and a manifest.json with the engine
version, the section list with record counts, a digest of the ontology, a digest
of every file, and a census of every locator that points outside the set
(with whether the source held its target). Time Machine history, soft-deleted
media versions, subtitles files and the per-installation counters are deliberately
not archived, and the manifest says so.
restore rebuilds the set in a database that need never have held it, and
refuses before writing anything on a digest mismatch, on an ontology node that
exists with a different definition, on a record that already exists, on a media
file that exists with different bytes, and — the cross-installation case — on a
locator that resolves neither to an archived record nor to a record the
destination holds. Each refusal has an explicit override flag; the dangling
locators written under --allow-external are reported, never silently re-pointed.
Every restored record receives one whole-record Time Machine row, so the restore
is visible in its history. Archive the sections that link to each other
together and no locator is external.
The format is defined in the repository file engineering/ARCHIVE_FORMAT.md and
verified by a reconstruction test that builds a section set with every component
model, extracts it, drops it, restores it from the artifact alone and asserts
byte equality on every column.
For developers
tools/tool_export/server/tool_export.ts(toolExportGetExportGrid()) is a pure facade over the unified diffusion export engine (src/diffusion/export/): record RESOLUTION rides the shared diffusion engine (compileExportPlanturnsar_ddo_to_exportinto aPublicationPlan; the diffusion resolver's atom entry point walks relation hops and stored locators), and the tool handler delegates to it in a single call. The client (flat_table.jsand friends) is vanilla JavaScript with an exact wire contract: one request shape, one NDJSON protocol, three data formats and a fixed set of options.The stream/buffered duality and the protocol shape (
metafirst, every row cell referencing an already-emitted column ordinal,endlast, its columns array a permutation of the emitted ordinals) are pinned bytest/unit/diffusion_export_unified.test.ts; correctness of the resolved values is pinned by the parity fixture replay (test/parity/tool_export_differential.test.tsandtest/parity/tool_export_breakdown_differential.test.ts).
The export pipeline
ar_ddo_to_export (chosen columns, user order)
│ POST dd_api:'dd_tools_api', action:'tool_request', source.action:'get_export_grid'
▼
src/core/tools/dispatch.ts dispatchToolRequest() — permission-gated per-tool registry
▼
tools/tool_export/server/tool_export.ts toolExportGetExportGrid()
│ resolves the SQO (search/sql_assembler.ts) then, per data_format,
│ walks each export ddo's path to atoms and mints columns/rows —
│ there is no separate per-component override class: the SAME
│ leaf-value resolver the relation_list panel uses
│ (resolve/relation_list.ts resolveCellValue/resolvePathValue) is
│ reused so both surfaces stay byte-identical.
▼ NDJSON (ndjson_stream:true) or a whole {meta,columns,rows} object
flat_table.js accumulate lines → preview + CSV/TSV/ODS/XLSX/HTML/media
The server forces the SQO to the full filtered selection (sqo.limit = null
→ ALL, offset = 0) after the read-permission gate, so the export always
covers the whole search result rather than the client's clamped page limit.
API
tool_export.get_export_grid(options) — dispatched through
dd_tools_api::tool_request (the RQO wire shape is unchanged). The TS module
(tools/tool_export/server/index.ts) declares the action's own permission
spec inline ({ permission: 'section', minLevel: 1, handler: ... }), enforced
by the generic per-tool dispatcher dispatchToolRequest()
(src/core/tools/dispatch.ts) — one explicit, typed dispatch table, shared
by every tool, that resolves the tool name, checks it is active and
authorized for the calling user, loads its server module, looks up the
action in that module's allowlist, and enforces the action's declarative
permission gate before running it. Request fields:
| Field | Meaning |
|---|---|
section_tipo |
Target section (read-permission gated). |
model |
'section'. |
data_format |
'value' | 'grid_value' | 'dedalo_raw'. |
breakdown |
'default' | 'rows' | 'columns' (used with grid_value). |
fill_the_gaps |
bool — repeat spanning values on exploded rows. |
value_with_parents |
bool — add ancestor-chain sibling columns (n/a for dedalo_raw). |
ar_ddo_to_export |
the columns, in output order. |
sqo |
the search query object (the selection to export). |
ndjson_stream |
bool — stream the flat-table protocol vs return it whole. |
The flat-table NDJSON protocol
The server emits newline-delimited JSON; each line is discriminated by t:
| Line | Shape | Purpose |
|---|---|---|
meta |
{t:'meta', v, data_format, breakdown, fill_the_gaps, section_tipo, total} |
Stream header. |
col |
{t:'col', i, key, group, path, label, ar_labels, cell_type, model, after} |
A column, emitted on first use. i is the stable ordinal cells reference; label is server-resolved; after hints live insertion. |
row |
{t:'row', rec, sub, c:{ordinal:value, …}} |
A (sub)row; c is sparse (ordinal→value); sub is the explosion index. |
end |
{t:'end', columns:[ordinal,…], rows, records} |
Authoritative display column order + counts. |
The column order in the output equals the order of ar_ddo_to_export, which in
the tool equals the order of the Active elements DOM list — i.e. the order the
user defined by dragging. cell_type (text | img | av | iri |
section_id | json) drives how flat_table.js renders each cell.
The component contract
Every component's export cell is resolved by ONE shared engine rather than a
per-model override method: tool_export.ts resolves every cell through the
SAME generic leaf-value walkers the relation_list panel uses
(resolvePathValue / resolveCellValue in src/core/resolve/relation_list.ts),
keyed on the export ddo's path. Per-model behavior is expressed
declaratively instead — each component model's descriptor carries a
flatValue facet that the shared walkers dispatch through, so relation
components recurse component-driven (export-atom child recursion) without
needing a bespoke override. Coverage is value, grid_value with all three
breakdown modes, dedalo_raw, multi-hop paths, NDJSON streaming, and
media/image cells — pinned by test/parity/tool_export_differential.test.ts,
test/parity/tool_export_breakdown_differential.test.ts and
test/unit/tool_export_relation_dataframe_fanout_native.test.ts. A genuinely new
component shape that the shared resolver cannot express needs its own case
added to the walkers rather than an override method.
- The flat-join (
valueformat) reference isresolvePathValue(). dedalo_rawcells are the exact stored value JSON-encoded with thededalo_datawrapper, always exactly that:{dedalo_data: <stored value>}. A component with dataframe slots grows one EXTRA column per slot, headed by the dataframe component's tipo and carrying that component's own stored frames — see The dedalo_data wrapper and Dataframe columns for the shared shape with the import side.
Files
tools/tool_export/server/tool_export.ts— request handling + the P6 routing seam (toolExportGetExportGrid); the legacy in-file build stays behindDEDALO_EXPORT_UNIFIED=falseuntil its ledgered deletion.src/diffusion/export/{compile_columns,atoms,grid,index}.ts— the unified build: column-set plan compile, shared-walk atoms, NDJSON grid emission (exportGridUnified).tools/tool_export/server/index.ts— the tool'sToolServerModuleregistration.src/core/resolve/relation_list.ts— the shared leaf-value resolvers (resolvePathValue,resolveCellValue) reused from the relation_list panel.tools/tool_export/js/flat_table.js— client accumulator, preview, downloads (copied as-is).tools/tool_export/js/{render_tool_export,drag_tool_export}.js— UI and drag-and-drop column model (copied as-is).tools/tool_export/js/export_user_presets.js,client/dedalo/core/section/js/view_export_user_presets.js— per-user presets (sectiondd1781; ordinary ontology data, no dedicated TS engine needed).