Map and materialize facts
Turn source rows into stored ontology entities and verify a readback
You need a Semogram account with access to the workspace and a project in it. Definition and binding changes, query publication, execution and assertion review require their respective permissions. External reads and writes also require the selected plugin installation's credentials and access.
What this workflow does
Ingress → Ontology mapping → Ontology materializer reads two orders, constructs entity/property/source-identity facts and stores them through Postgres's ontology-store capability. Mapping produces a fact set; materialization persists it. A successful mapping step alone proves neither persistence nor query visibility.
Prepare a known source
Use a test PostgreSQL database reachable from Semogram's execution runtime. In its SQL client, create the fixture below. These are database instructions, not text to paste into Semogram's endpoint form.
CREATE TABLE public.ontology_demo_orders (
entity_id text PRIMARY KEY,
order_id text NOT NULL,
status text NOT NULL,
total numeric(12,2) NOT NULL
);
INSERT INTO public.ontology_demo_orders VALUES
('https://example.com/orders/1', 'O-001', 'open', 120.00),
('https://example.com/orders/2', 'O-002', 'delivered', 75.50);
SELECT * FROM public.ontology_demo_orders ORDER BY entity_id;Open workspace Plugins → Explore, select Postgres (@craven/postgres) and install its read capability. Enter your database Host, Port (usually 5432), Database, User, Password and Ssl setting. The user needs SELECT access to the fixture. Enter credentials in the protected installation controls and check the connection. More options are explained in the Postgres installation guide.
Create a workspace Data Endpoint named demo_orders, namespace ontology_demo, direction Ingress, using that installed read capability and the source_stream contract. Set target Schema public, Table ontology_demo_orders. Save and inspect the two records. The endpoint name is chosen in Semogram; public.ontology_demo_orders is the physical database table. Keep the returned endpoint UUID. No import or materialization is needed for a live read.
Prepare the model
Create project definition operations-demo, namespace https://example.com/operations#, with the complete model.ttl as its model entrypoint and no shapes/imports. Save/validate it and inspect the compiled package UUID and numeric version. This model declares Order, orderId, status and total with string/string/decimal ranges. Use the exact full IRIs below; labels are not interchangeable with compiled identifiers.
Prepare the fact-store capability
Use a reachable test PostgreSQL database. In workspace Plugins → Explore, install Postgres (@craven/postgres) ontology-store, a fact-store capability distinct from raw-table read/write. Enter Host, Port (usually 5432), Database, User, Password and Ssl in the installation controls. Its database user needs the schema/table creation, materialization and read privileges required by the plugin. Check the connection. The Postgres guide explains installation options.
Create a dedicated schema using your database SQL client:
CREATE SCHEMA IF NOT EXISTS ontology_demo_facts;Create workspace endpoint demo_facts, namespace ontology_demo, direction Materialization, contract ontology_fact_store, selecting the installed fact-store capability. Set Type postgres_ontology_fact_store, Schema ontology_demo_facts, Table prefix orders. Save/check it and keep its actual endpoint UUID. These settings select generated fact storage, not the source orders table. Saving the endpoint does not insert facts.
Build the pipeline
In this project's Pipeline Studio, create an Ingress using demo_orders, Full load/parser mode, output orders. Connect it to Ontology mapping, then to Ontology materializer. Select the compiled package on both ontology nodes. Connect the mapping's fact-set output to the materializer input; edges and ports belong to the full pipeline graph.
Map one Order per source row using the ID template {entity_id}. Map orderId from order_id as string, status from status as string, and total from total as number, using full property IRIs. Add source identity namespace ontology-demo-postgres, key order_id, value from order_id. Select demo_facts on the materializer and review upsert and validation.
Build a pipeline that reads ontology_demo.demo_orders and maps the two rows into https://example.com/operations#Order entities. Preserve entity_id exactly. Map orderId, status and total using their full IRIs and the types on this page, preserve source identity, and materialize through ontology_demo.demo_facts. Show store, package, mappings, edges and validation before saving.These are node configuration sections inside a complete pipeline document, not standalone API requests. Replace resource placeholders with actual IDs.
{
"packageId": "<COMPILED_PACKAGE_UUID>",
"inputs": {
"orders": "orders"
},
"output": "order_facts",
"mode": "upsert",
"entities": [
{
"class": "https://example.com/operations#Order",
"id": {
"template": "{entity_id}"
},
"properties": {
"https://example.com/operations#orderId": {
"from": "order_id",
"as": "string"
},
"https://example.com/operations#status": {
"from": "status",
"as": "string"
},
"https://example.com/operations#total": {
"from": "total",
"as": "number"
}
},
"identities": [
{
"sourceNamespace": "ontology-demo-postgres",
"sourceKey": "order_id",
"sourceId": {
"from": "order_id"
}
}
],
"provenance": {
"system": "ontology-demo"
}
}
],
"relationships": []
}{
"packageId": "<COMPILED_PACKAGE_UUID>",
"inputs": {
"facts": "order_facts"
},
"dataEndpointId": "<FACT_STORE_ENDPOINT_UUID>",
"mode": "upsert",
"validation": {
"unknownTerms": "reject_fact_set",
"invalidRecords": "reject_fact_set",
"cardinality": "enforce",
"datatype": "enforce"
}
}Validate the draft, save a version, inspect the active version and run once. Inspect both mapping and materializer outputs, stored counts, errors and provenance. Programmatic execution uses HTTP POST /pipelines/<PIPELINE_UUID>/execute (pipelines:execute, Idempotency-Key) or MCP pipeline_execute with pipelineId/idempotencyKey. These call an existing saved graph; they do not accept the node sections above as the whole pipeline.
Download the complete graph create body. Its UUIDs are illustrative: replace metadata project/workspace IDs, the ingress dataEndpointId, both packageId values and the materializer dataEndpointId with your real resources. Submit the complete document using the supported pipeline authoring methods, not just the node sections.
Bind the stored facts for reads
In Ontology → Bindings, inspect any automatically created default binding. If no correct one exists, create an Active Materialized wildcard binding for this compiled package/version and demo_facts: Business term *, Applies to *, Expected values Unknown, default enabled, resolution {}. Avoid a conflicting default; inspect the validation report.
Select the compiled package and demo_facts endpoint; fill the wildcard fields above. Review existing bindings and validation before saving. A more specific binding can override this default.
Use a workspace API key with bindings:write. Set SEMOGRAM_API_KEY in your shell and replace UUID placeholders with accessible resource IDs.
curl --request POST "https://platform.semogram.com/api/v1/projects/<PROJECT_UUID>/ontology/read-bindings" \
--header "Authorization: Bearer ${SEMOGRAM_API_KEY}" \
--header "Idempotency-Key: <UNIQUE_OPERATION_KEY>" \
--header "Content-Type: application/json" \
--data-binary @- <<'JSON'
{
"ontologyPackageId": "<COMPILED_PACKAGE_UUID>",
"ontologyPackageVersion": 1,
"termRef": "*",
"termKind": "*",
"kind": "materialized",
"dataEndpointId": "<FACT_STORE_ENDPOINT_UUID>",
"status": "active",
"isDefault": true,
"cardinality": "unknown",
"resolution": {}
}
JSON{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "ontology_read_binding_create",
"arguments": {
"projectId": "<PROJECT_UUID>",
"binding": {
"ontologyPackageId": "<COMPILED_PACKAGE_UUID>",
"ontologyPackageVersion": 1,
"termRef": "*",
"termKind": "*",
"kind": "materialized",
"dataEndpointId": "<FACT_STORE_ENDPOINT_UUID>",
"status": "active",
"isDefault": true,
"cardinality": "unknown",
"resolution": {}
},
"idempotencyKey": "<UNIQUE_STORE_BINDING_KEY>"
}
}
}Replace numeric version 1 with the actual compiled version. Definition compilation may create a suitable default, but it never runs this materialization.
Verify through a complete query
Create Demo stored orders under Ontology → Queries using this complete YAML, with actual package UUID and numeric version. Validate, save and publish its reviewed version. Execute with no inputs and limit 10 using UI, HTTP POST /ontology/queries/<QUERY_UUID>/execute or MCP query_execute.
query_definition:
name: Demo stored orders
slug: demo-stored-orders
ontology_package_id: <COMPILED_PACKAGE_UUID>
ontology_package_version: <COMPILED_PACKAGE_VERSION_NUMBER>
target:
kind: materialized
input_schema:
type: object
additionalProperties: false
root:
term_ref: https://example.com/operations#Order
cardinality: many
output_schema:
type: object
properties:
id: { type: string }
order_id: { type: string }
status: { type: string }
total: { type: number }
required: [id, order_id, status, total]
output_mapping:
id: { term_ref: https://example.com/operations#Order }
order_id: { term_ref: https://example.com/operations#orderId }
status: { term_ref: https://example.com/operations#status }
total: { term_ref: https://example.com/operations#total }Expect IDs https://example.com/orders/1 and /2, order IDs O-001 and O-002, statuses open and delivered, totals 120 and 75.5. Compare by ID. Inspect completed query rows, resolved binding endpoint, completeness, diagnostics and source snapshots; compare the stored source identities and producing run as well.
If no rows appear, check the materializer result, selected store and root binding before rerunning. Repeating an operation can have external effects; choose supported atomicity/conflict semantics rather than assuming rollback. A successful source read is not evidence of a successful store write.