Skip to learning content
← All articles
RAG free

Build a working ontology from defence logistics data

Build an ontology from an empty Python file, inspect actual graph records and their sources, and extend a maintenance example to multiple required parts. Six chapters combine plain-language explanations with a downloadable, locally tested implementation.

The question: How do we actually create, validate and use an ontology from operational source records?

You’ll learn: Construct an ontology step by step, trace RDF facts to CSV records, validate and query them, and migrate the model to support several required parts per order.

For: Domain experts, analysts and engineers; concepts are explained before the runnable commands.

Executed teaching prototype · AI-assisted source check . This is not a human expert endorsement.

Chapter 4: Inspect the real graph and define the integration contract

4. Inspect the real graph and define the integration contract#

You now have a populated graph and a checked result. This chapter connects the two: follow WO-204 to its asset and part, inspect the stock and allocations, and open the source rows behind those facts. Then define the contract another application would need to use the same model.

Step 1: Explore the records that were actually built#

The explorer below contains an export of the verified, fictional ORG-TRAIN baseline graph. It is generated from the lab artifacts. It is not a drawing of records that might exist. Selecting a record changes only this page; the viewer makes no model or business-system calls.

Explore the generated records

Follow WO-204 from graph to source

These 7 records and 6 relationships come from the lab’s generated RDF. Select a record, follow a relationship, then inspect the original source row.

Fictional scope: ORG-TRAIN · Saved comparison time: . This is a fixed teaching snapshot.

On a narrow screen, scroll the graph sideways. Every record is also available in the selector below.

WO-204 WorkOrder

org
ORG-TRAIN
recordId
WO-204
requiredQuantity
2integer
unit
each
Follow a relationship
  • WO-204 → asset →
  • WO-204 → part →
Inspect the exported build identity

Schema version: 1.0.0

Input fingerprint: 51b127fe0d01615e8c3137bf0c1b8dc45983b01c1ce0ea5d6b015258520d629f

Importer SHA-256: 0f956fe682b1a33f108cfd5420a34a29ddb479e96c12c2b75cdc2650aea080ff

Exporter SHA-256: de404043247cc314794a17c1cecd59f34b7c6f6ddaefcdd291dac2fc4bb476bf

Start at WO-204. Follow its asset relationship to GEN-17 and its part relationship to PF-9. Inspect the property requiredQuantity, then open its orders.csv source record. Follow a stock snapshot and its allocations to see which values supplied the shortage calculation.

Notice the difference between a relationship and a literal property: m:part points to another identified record; m:requiredQuantity contains an integer. The source row uses FILTER_PF9, while the graph uses the canonical PF-9 identity. The alias record explains that transformation. File hashes identify the bytes used for this export; their presence does not independently certify the source.

Step 2: Ask the graph directly#

For a small executable inspection, save the following as inspect_graph.py in the lab directory after Chapter 3's build:

python
from rdflib import Graph

graph = Graph().parse("results/instances.ttl", format="turtle")
query = """
PREFIX m: <https://example.org/maintenance/>
SELECT ?asset ?part ?needed ?unit
WHERE {
  ?order a m:WorkOrder ;
    m:org "ORG-TRAIN" ;
    m:recordId "WO-204" ;
    m:asset ?asset ;
    m:part ?part ;
    m:requiredQuantity ?needed ;
    m:unit ?unit .
}
"""
for row in graph.query(query):
    print(tuple(str(term) for term in row))

Run .\.venv\Scripts\python.exe inspect_graph.py. The supplied fixture returns the GEN-17 asset IRI, PF-9 part IRI, 2, and each. Each ?name is a variable; the patterns find records having all of the stated relationships and values. SELECT chooses the returned columns.401 An empty result means this pattern found no match, so first inspect the identifiers and mapping.

The inspection query only reads relationships. Run the full business query to apply the availability and evidence checks:

powershell
.\.venv\Scripts\python.exe ontology_lab.py query --org ORG-TRAIN --order WO-204

Open queries/order_inventory.rq. It binds the organization and order ID through RDFLib's initBindings, joins the matching asset, part, and stock, then sums allocations by kind. The code requires exactly one matching snapshot. It does not combine depots or identify which work order owns each reservation. The --org argument selects a local demonstration scope; it is not a login.

Establish quantities at their own grain: one stock snapshot and its allocations. Joining every inspection record into that sum could repeat the stock row and inflate it. DISTINCT cannot repair every bad join because two legitimate allocations can have the same quantity. Attach descriptive evidence after calculating the correctly scoped quantities.

Step 3: Specify each field and relationship#

A reusable integration needs more than class names. For the baseline, these are actual mappings and cardinalities; “one” means exactly one value on each accepted record. SHACL checks selected cardinalities, while the importer and query supply additional business checks.

Source fieldsModel destinationType and multiplicity
orders.csv: order_id, orgWorkOrder identity and scopeOne canonical ID and one organization
asset_refWorkOrder m:asset → AssetOne resolved asset
part_ref, required_quantity, unitWorkOrder m:part, m:requiredQuantity, m:unitOne part; positive integer; each
inventory.csv: snapshot_id, part_ref, depotStockSnapshot identity, m:part, m:depotOne part and one depot value
on_hand, unit_ids, observed_at, revisionSnapshot quantity, physical unit values, time, revisionNonnegative integer; distinct unit IDs; timezone-aware timestamp; positive revision
allocations.csv: snapshot_id, kind, quantityAllocation m:snapshot, m:kind, m:quantityOne snapshot; reserved or quarantined; nonnegative integer
inspections.csv: asset_ref, status, source_versionEvidence m:asset, m:status, m:sourceVersionOne asset, enumerated status, and source-version string

The record key is the full IRI built from organization, entity kind, and canonical ID. It is not a display name. A WorkOrder has one asset; an Asset may have several orders. Each Allocation has one snapshot; a snapshot may have several allocations. The baseline's one-part WorkOrder constraint is deliberate and is replaced with requirement-line objects in Chapter 6.

Document who may establish or revise each fact. The following ownership and update rules are a proposed integration contract for a real pilot, rather than services supplied by the local lab:

Producer and ownerAuthoritative contributionUpdate rule to implement
Equipment register / asset stewardAsset identity and approved aliasesReview identity changes; preserve existing references
Maintenance planning / job ownerWork-order requestVersion changed requirements; do not infer approval from creation
Stock control / depot ownerCounts, unit identities, allocation statePublish a coherent snapshot; reject conflicting revisions
Inspection register / evidence ownerObservation, status, source versionPreserve the observed time and source revision

Object, property, and relationship documentation provides useful implementation concepts, but it does not choose these domain contracts for you.403404405 The keys, source meanings, cardinalities, and ownership decisions above must be reviewed against the actual organization.

Step 4: Choose components for concrete responsibilities#

What actually runs in the downloadable implementation

Original architecture image. Solid connections show the local executable path. The bottom row identifies services a deployed application would still need; those services are not silently claimed as part of the lab.

Select a box to inspect its role. On a small screen, scroll the diagram sideways.
ingestwritevalidategateanswer
Versioned input files

The bounded sample data and reviewed identity map are the lab inputs. Real source connectors would need ownership, change detection and access policy.

Read the complete diagram as text
  • Versioned input files: The bounded sample data and reviewed identity map are the lab inputs. Real source connectors would need ownership, change detection and access policy.
  • Python + RDFLib: The build command creates the RDF/OWL model and maps records into a graph. There is no graph-database service or LLM API hidden behind this step.
  • Ontology + instances: Schema, instance data and provenance are concrete files that can be inspected independently of the website. A database service becomes a separate deployment choice.
  • Query & review output: The sample query computes availability and shortage and emits its explanation. This output supports a review; it does not issue a command to a maintenance or inventory system.
  • RDFLib SPARQL: The query path uses explicit organization scope and typed relationships. The CLI scope selector is a test boundary, not enterprise authentication.
  • pySHACL validation: Run the supplied shape and integrity checks before using the graph. Keep the report and failed-record evidence.
  • Deployment: identity: Replace the demonstration scope selector with verified principals and server-enforced policy. Protect data, derived outputs, logs and exports.
  • Deployment: workflow: A durable scheduler and controlled command handler would manage publication, retries, approval expiry and downstream changes. They require their own tests.
  • Deployment: operations: Backups, monitoring, schema migration, secret handling and approved network boundaries are additional operational responsibilities.
  • Versioned input files → ingest → Python + RDFLib
  • Python + RDFLib → write → Ontology + instances
  • Ontology + instances → validate → pySHACL validation
  • pySHACL validation → gate → RDFLib SPARQL
  • RDFLib SPARQL → answer → Query & review output
Component in the labResponsibilityAdd a service when…
CSV and JSON fixturesRepeatable source recordsApproved systems must publish updates
Python importerIdentity resolution and typed mappingsUpdates need scheduling, retries, and quarantine queues
RDFLib and RDF filesStore inspectable statements and execute SPARQL402Concurrent access, query volume, or availability requires a managed store
pySHACL plus business checksReject specified invalid recordsSeveral producers need a shared ingestion release gate
Query and review JSONExplain a bounded requestAuthenticated users need controlled views or approved actions

This architecture keeps source interpretation separate from query logic. If you later replace file storage with a graph service, use the same fixture and expected results to check the migration before measuring throughput.

Step 5: Reuse the model for a second question#

Chapter 6 asks which requirement lines of WO-204 are short, and which stock and allocation records explain each line? Its new PartRequirement object owns a part and quantity together. Adding a seal to the filter requirement then produces two independently inspectable paths.

That distinction matters: two m:part values and two quantities placed directly on one WorkOrder would not say which quantity belongs to which part. Giving each line an identity removes that ambiguity. The extension keeps the existing asset, part, stock, and evidence relationships and changes the portion of the model that the new question requires.

Checkpoint: what you can inspect and hand over#

You can trace the actual graph to a CSV record, run a relationship query, explain the aggregation grain, and provide a source-to-model contract. Check that the explorer and results/explanation.json agree on six on hand, two quarantined, three reserved, one available, and one short. Next, preserve that result through an explicit orchestration sequence.

Source 401 ↗

W3C, SPARQL 1.1 Query Language — graph patterns and result columns; checked 14 September 2026. The query is original to this lab.

Source 402 ↗

RDFLib, Getting started — graph parsing and queries; checked 14 September 2026.

Source 403 ↗

Object types and instances — operational model concepts; checked 14 September 2026.

Source 404 ↗

Declared property types — property modelling; checked 14 September 2026.

Source 405 ↗

Relationships between object types — link modelling; checked 14 September 2026. The maintenance mapping and proposed ownership rules are this guide's design.