Version: 1.0 Last Updated: 2025-11-12 Status: Specification Purpose: Define semantic interoperability between Exocortex's ExoRDF framework and W3C RDF/RDFS standards
- Overview
- Why Semantic Interoperability Matters
- Class Mappings
- Property Mappings
- URI Construction Strategy
- RDF Triple Examples
- SPARQL Query Examples
- Inference and Reasoning
- Implementation Notes
- References
ExoRDF (Exocortex RDF Framework) is a custom ontology for knowledge management built on semantic web principles. It extends standard RDF/RDFS with domain-specific concepts for personal knowledge management, task tracking, and information organization.
| Prefix | Namespace IRI | Purpose |
|---|---|---|
exo: |
https://exocortex.my/ontology/exo# |
Core universal properties (all assets) |
ems: |
https://exocortex.my/ontology/ems# |
Effort Management System (tasks, projects) |
rdf: |
http://www.w3.org/1999/02/22-rdf-syntax-ns# |
RDF core vocabulary |
rdfs: |
http://www.w3.org/2000/01/rdf-schema# |
RDF Schema vocabulary |
owl: |
http://www.w3.org/2002/07/owl# |
Web Ontology Language |
xsd: |
http://www.w3.org/2001/XMLSchema# |
XML Schema datatypes |
ExoRDF classes and properties are subclasses and subproperties of standard RDF/RDFS concepts, not equivalents. This preserves:
- Semantic extension: ExoRDF adds domain-specific meaning
- Backward compatibility: Standard RDF/RDFS queries still work
- Interoperability: Tools understanding RDF/RDFS can process Exocortex data
- Inference: SPARQL can use transitive relationships (
rdfs:subClassOf*)
- Standard SPARQL queries: Use
rdf:typeinstead of custom predicates - Tool compatibility: Export to semantic web tools (Protégé, OntoGraf, etc.)
- Reasoning support: Infer relationships via class/property hierarchies
- Future-proofing: Align with semantic web ecosystem
- Clear semantics: Explicit relationships to W3C standards
- Validation: Use standard RDF validators
- Reusable patterns: Leverage existing RDF/RDFS patterns
- Interoperability: Integrate with other semantic systems
User wants all tasks (including subtypes):
# ❌ WITHOUT mapping: Must know all task subtypes
SELECT ?task WHERE {
?task exo:Instance_class ?type .
FILTER (?type IN (ems:Task, ems:Subtask, ems:Milestone))
}
# ✅ WITH mapping: Automatic via rdfs:subClassOf
SELECT ?task WHERE {
?task rdf:type/rdfs:subClassOf* ems:Task .
}ExoRDF defines classes that are subclasses of RDF/RDFS classes.
# ExoRDF classes extend RDF/RDFS classes
exo:Asset rdfs:subClassOf rdfs:Resource .
exo:Class rdfs:subClassOf rdfs:Class .
exo:Property rdfs:subClassOf rdf:Property .
# EMS classes extend exo:Asset
ems:Task rdfs:subClassOf exo:Asset .
ems:Project rdfs:subClassOf exo:Asset .
ems:Area rdfs:subClassOf exo:Asset .| ExoRDF Class | RDF/RDFS Superclass | Relationship | Rationale |
|---|---|---|---|
exo:Asset |
rdfs:Resource |
rdfs:subClassOf |
All Exocortex entities are RDF resources |
exo:Class |
rdfs:Class |
rdfs:subClassOf |
ExoRDF classes are also RDF classes |
exo:Property |
rdf:Property |
rdfs:subClassOf |
ExoRDF properties are also RDF properties |
ems:Task |
exo:Asset |
rdfs:subClassOf |
Tasks are specialized assets |
ems:Project |
exo:Asset |
rdfs:subClassOf |
Projects are specialized assets |
ems:Area |
exo:Asset |
rdfs:subClassOf |
Areas are specialized assets |
rdfs:Resource
└── exo:Asset
├── ems:Task
├── ems:Project
└── ems:Area
rdfs:Class
└── exo:Class
rdf:Property
└── exo:Property
ExoRDF properties are subproperties of RDF/RDFS properties.
| ExoRDF Property | RDF/RDFS Superproperty | Relationship | Purpose |
|---|---|---|---|
exo:Instance_class |
rdf:type |
rdfs:subPropertyOf |
Asset type classification |
exo:Asset_isDefinedBy |
rdfs:isDefinedBy |
rdfs:subPropertyOf |
Ontology reference |
exo:Class_superClass |
rdfs:subClassOf |
rdfs:subPropertyOf |
Class hierarchy |
exo:Property_range |
rdfs:range |
rdfs:subPropertyOf |
Property value type |
exo:Property_domain |
rdfs:domain |
rdfs:subPropertyOf |
Property applies to |
exo:Property_superProperty |
rdfs:subPropertyOf |
rdfs:subPropertyOf |
Property hierarchy |
# Property hierarchy triples (generated once at initialization)
exo:Instance_class rdfs:subPropertyOf rdf:type .
exo:Asset_isDefinedBy rdfs:subPropertyOf rdfs:isDefinedBy .
exo:Class_superClass rdfs:subPropertyOf rdfs:subClassOf .
exo:Property_range rdfs:subPropertyOf rdfs:range .
exo:Property_domain rdfs:subPropertyOf rdfs:domain .
exo:Property_superProperty rdfs:subPropertyOf rdfs:subPropertyOf .For each asset, the triple store generates both ExoRDF and RDF/RDFS triples:
Input (Obsidian frontmatter):
exo__Instance_class: ems__TaskOutput (RDF triples):
# ExoRDF triple (custom predicate)
<asset-uri> exo:Instance_class ems:Task .
# RDF/RDFS triple (standard predicate)
<asset-uri> rdf:type ems:Task .Why both?
- ExoRDF triple: Preserves original property name for tooling
- RDF/RDFS triple: Enables standard SPARQL queries and inference
Format: http://${ontology_url}/${asset_uid}
Components:
${ontology_url}: From asset'sexo__Asset_isDefinedByontology file${asset_uid}: Asset'sexo__Asset_uid(UUID v4)
| Approach | Stability | Uniqueness | Semantic Web Compatibility |
|---|---|---|---|
| Filename-based | ❌ Breaks on rename | ❌ Non-standard | |
| UID-based | ✅ Stable across renames | ✅ Globally unique | ✅ Standard practice |
Rationale:
- Stability: UIDs never change, filenames can be renamed
- Uniqueness: UUID v4 provides global uniqueness (2^122 possible values)
- Semantic Web: Standard practice in RDF systems (DBpedia, Wikidata, etc.)
function constructAssetURI(asset: AssetMetadata): string {
// 1. Extract UID
const uid = asset.frontmatter?.exo__Asset_uid;
if (!uid) {
throw new Error("Missing exo__Asset_uid");
}
// 2. Resolve ontology URL
const ontologyRef = asset.frontmatter?.exo__Asset_isDefinedBy; // "[[Ontology/EMS]]"
const ontologyFile = vault.getFileByPath(extractWikiLink(ontologyRef));
const ontologyMetadata = readFrontmatter(ontologyFile);
const ontologyURL = ontologyMetadata?.exo__Ontology_url || "https://exocortex.my/default/";
// 3. Construct URI
const baseURL = ontologyURL.endsWith("/") ? ontologyURL : `${ontologyURL}/`;
return `${baseURL}${uid}`;
}Asset frontmatter:
exo__Asset_uid: 550e8400-e29b-41d4-a716-446655440000
exo__Asset_isDefinedBy: "[[Ontology/EMS]]"
exo__Instance_class: ems__TaskOntology frontmatter (Ontology/EMS.md):
exo__Ontology_url: https://exocortex.my/ontology/ems/Result:
URI: https://exocortex.my/ontology/ems/550e8400-e29b-41d4-a716-446655440000
| Scenario | Handling |
|---|---|
Missing exo__Asset_uid |
Throw error (strict mode) or use fallback (filename) with warning |
Missing exo__Asset_isDefinedBy |
Use default ontology URL: https://exocortex.my/default/ |
| Invalid ontology URL | Validate HTTP(S) protocol, throw error if invalid |
| Ontology file not found | Use default ontology URL with warning |
Obsidian Note (Review PR #365.md):
---
exo__Asset_uid: 550e8400-e29b-41d4-a716-446655440000
exo__Instance_class: ems__Task
exo__Asset_label: Review PR #365
exo__Asset_isDefinedBy: "[[Ontology/EMS]]"
ems__Effort_status: "[[ems__EffortStatusInProgress]]"
ems__Task_size: M
---Generated RDF Triples:
@prefix exo: <https://exocortex.my/ontology/exo#> .
@prefix ems: <https://exocortex.my/ontology/ems#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
<https://exocortex.my/ontology/ems/550e8400-e29b-41d4-a716-446655440000>
# ExoRDF triples
exo:Instance_class ems:Task ;
exo:Asset_label "Review PR #365" ;
exo:Asset_isDefinedBy <https://exocortex.my/ontology/ems/ontology-uid> ;
ems:Effort_status ems:EffortStatusInProgress ;
ems:Task_size "M" ;
# RDF/RDFS triples (additional, for interoperability)
rdf:type ems:Task ;
rdfs:label "Review PR #365" ;
rdfs:isDefinedBy <https://exocortex.my/ontology/ems/ontology-uid> .Project Note (Implement SPARQL Engine.md):
---
exo__Asset_uid: 7c9e6679-7425-40de-944b-e07fc1f90ae7
exo__Instance_class: ems__Project
exo__Asset_label: Implement SPARQL Engine
exo__Asset_isDefinedBy: "[[Ontology/EMS]]"
---Task Note (Fix SPARQL Parser.md):
---
exo__Asset_uid: 3b241101-e2bb-4255-8caf-4136c566a964
exo__Instance_class: ems__Task
ems__Effort_parent: "[[Implement SPARQL Engine]]"
---Generated RDF Triples:
# Project
<https://exocortex.my/ontology/ems/7c9e6679-7425-40de-944b-e07fc1f90ae7>
rdf:type ems:Project ;
exo:Instance_class ems:Project ;
rdfs:label "Implement SPARQL Engine" .
# Task
<https://exocortex.my/ontology/ems/3b241101-e2bb-4255-8caf-4136c566a964>
rdf:type ems:Task ;
exo:Instance_class ems:Task ;
ems:Effort_parent <https://exocortex.my/ontology/ems/7c9e6679-7425-40de-944b-e07fc1f90ae7> .Using ExoRDF predicates:
PREFIX exo: <https://exocortex.my/ontology/exo#>
PREFIX ems: <https://exocortex.my/ontology/ems#>
SELECT ?task ?label
WHERE {
?task exo:Instance_class ems:Task ;
exo:Asset_label ?label .
}Using RDF/RDFS predicates (after mapping):
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX ems: <https://exocortex.my/ontology/ems#>
SELECT ?task ?label
WHERE {
?task rdf:type ems:Task ;
rdfs:label ?label .
}Query all asset types via rdfs:subClassOf inference:
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX exo: <https://exocortex.my/ontology/exo#>
SELECT ?asset ?type ?label
WHERE {
?asset rdf:type ?type .
?type rdfs:subClassOf* exo:Asset .
OPTIONAL { ?asset rdfs:label ?label }
}Results (includes tasks, projects, areas):
asset type label
===================================================================================================
https://exocortex.my/ontology/ems/550e8400-e29b-41d4... ems:Task "Review PR #365"
https://exocortex.my/ontology/ems/7c9e6679-7425-40de... ems:Project "Implement SPARQL"
https://exocortex.my/ontology/ems/a1b2c3d4-e5f6-7890... ems:Area "Development"
Find all subclasses of exo:Asset:
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX exo: <https://exocortex.my/ontology/exo#>
SELECT ?subclass
WHERE {
?subclass rdfs:subClassOf+ exo:Asset .
}Results:
subclass
==================
ems:Task
ems:Project
ems:Area
Find all superproperties of exo:Instance_class:
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX exo: <https://exocortex.my/ontology/exo#>
SELECT ?superproperty
WHERE {
exo:Instance_class rdfs:subPropertyOf+ ?superproperty .
}Results:
superproperty
========================
rdf:type
rdfs:subPropertyOf
For more RDF/RDFS query patterns and examples, see:
- SPARQL User Guide - Section "Using Standard RDF/RDFS Vocabulary"
- SPARQL Query Examples - Section "RDF/RDFS Standard Queries" (10+ examples)
- SPARQL Developer Guide - Section "ExoRDF to RDF/RDFS Mapping Architecture"
- SPARQL Performance Tips - Section "RDF/RDFS Inference Performance"
When RDF/RDFS mapping is active, SPARQL queries support these inference capabilities:
Rule: If A rdfs:subClassOf B and B rdfs:subClassOf C, then A rdfs:subClassOf C.
Example:
# Find all resources (including tasks via ems:Task rdfs:subClassOf exo:Asset rdfs:subClassOf rdfs:Resource)
SELECT ?resource WHERE {
?resource rdf:type/rdfs:subClassOf* rdfs:Resource .
}Rule: If P rdfs:subPropertyOf Q and Q rdfs:subPropertyOf R, then P rdfs:subPropertyOf R.
Example:
# Find all instances (using exo:Instance_class → rdf:type chain)
SELECT ?instance ?type WHERE {
?instance ?property ?type .
?property rdfs:subPropertyOf rdf:type .
}Rule: If P rdfs:domain C and ?s P ?o, then ?s rdf:type C.
Example:
# Automatically infer asset types from properties used
SELECT ?asset WHERE {
?asset ems:Effort_status ?status . # Implies ?asset rdf:type ems:Task (if ems:Effort_status rdfs:domain ems:Task)
}| Inference Type | Complexity | Performance |
|---|---|---|
| Direct property/class lookup | O(1) | < 1ms (index-based) |
rdfs:subClassOf (1 level) |
O(n) | < 10ms (n = subclasses) |
rdfs:subClassOf* (transitive) |
O(n²) | < 100ms (n = hierarchy depth) |
| Full graph reasoning | O(2^n) | > 1s (requires reasoning engine) |
Recommendation: Use rdfs:subClassOf* sparingly in production queries. Cache results when possible.
The mapping implementation must not depend on Obsidian-specific APIs. It should work in:
- ✅ Obsidian plugin (via
IFileSystemAdapter) - ✅ CLI tool (reading markdown files directly)
- ✅ Web interface (via API)
-
Initialization Phase (once):
- Generate class hierarchy triples (
exo:Asset rdfs:subClassOf rdfs:Resource) - Generate property hierarchy triples (
exo:Instance_class rdfs:subPropertyOf rdf:type)
- Generate class hierarchy triples (
-
Asset Loading Phase (per asset):
- Generate ExoRDF triples (custom predicates)
- Generate mapped RDF/RDFS triples (standard predicates)
Expected memory increase: ~15-20% (dual triples for mapped properties).
Example:
- 1000 assets × 10 properties each = 10,000 ExoRDF triples
- ~6 mapped properties × 1000 assets = 6,000 additional RDF/RDFS triples
- Total: 16,000 triples (~15% increase)
- ✅ Existing SPARQL queries using
exo:Instance_classcontinue to work - ✅ New queries can use
rdf:typefor standard compatibility - ✅ Both predicates return same results (dual triples)
- Property Schema Reference - Complete frontmatter property definitions
- SPARQL User Guide - Query syntax and examples
- SPARQL Developer Guide - API and architecture
- #366: Implement UID-Based URI Construction
- #367: Integrate ExoRDF to RDF/RDFS Mapping into Triple Store
- #368: Add Tests for ExoRDF to RDF/RDFS Mapping
| Version | Date | Changes |
|---|---|---|
| 1.0 | 2025-11-12 | Initial specification |
Next Steps: This specification defines the mapping. Implementation happens in:
- Issue #366: URI construction utilities
- Issue #367: Triple store integration
- Issue #368: Comprehensive tests