RDF Export
NeoWiki projects its data to RDF in its own vocabulary — the native projection. Each wiki page becomes a named graph holding its page metadata and its Subjects (one RDF resource each) with their Statements and Relations.
This page is the reference for what NeoWiki emits. Why the projection is shaped this way, and what is still open about it, is in NativeRdfProjection.md.
For an end-to-end example comparing the native and ontology-mapped output, see the Person-to-EDM worked example.
Configuration
| Setting | Default | Purpose |
|---|---|---|
$wgNeoWikiRdfBaseUri | the wiki's canonical URL ($wgCanonicalServer) | Base URI under which all NeoWiki IRIs are minted. |
$wgNeoWikiDereferenceSubjectsToDataTab | true | Whether a browser dereferencing a Subject IRI lands on the hosting page's Data tab row (true) or the plain page (false). |
IRI scheme
All NeoWiki IRIs live under $base ($wgNeoWikiRdfBaseUri). Standard vocabulary (rdf:, rdfs:, xsd:, dcterms:) is used for standard concepts.
| Prefix | Namespace | Used for |
|---|---|---|
neo: | $base/ontology/ | NeoWiki vocabulary terms (neo:Page, neo:Relation, neo:hasSubject, neo:source, …) |
neo-subj: | $base/entity/ | Subject IRIs (neo-subj:s1demo8aaaaaab5) |
neo-prop: | $base/prop/ | Property and Relation-type predicates (neo-prop:Has_author) |
neo-schema: | $base/schema/ | Schema classes (neo-schema:Person) |
neo-rel: | $base/relation/ | Relation node IRIs (neo-rel:r1demo8aaaaaaD6) |
neo-page: | $base/page/ | Page resource IRIs (neo-page:42) — the subject of the page-metadata triples |
Each page's named-graph IRI is $base/graph/{projection}/page/{id}. The {projection} segment is native or a Mapping page name (e.g. EDM), encoded like the names below — see Ontology Mapping. The page resource IRI (neo-page:42) stays projection-independent and appears inside the triples.
Nothing is written to the default graph. Stores that fold their named graphs into it answer unscoped queries anyway — QLever always does, and Oxigraph does with --union-default-graph — and on a store that does not, a query reaches page data only through GRAPH. Scope each pattern by the graph holding it rather than wrapping a whole query in one GRAPH block: patterns sharing a block must match within a single page.
Property, Schema, and Relation-type names, and Relation-property keys, form the local part of their IRI: spaces become underscores (Has author → neo-prop:Has_author), and characters illegal in an IRI (%, < > " { } | ^ \, backtick, control characters) are percent-encoded. Non-ASCII Unicode is kept raw. Caveat: the space→underscore step collides when a name already contains an underscore — Has author and Has_author share the neo-prop:Has_author IRI, which the native projection accepts. The base URI is trusted admin config and is not encoded.
A url value projects as an IRI object (<https://…>); a value that is not a valid absolute IRI falls back to an xsd:anyURI literal, so nothing is lost. The other value types map to xsd datatypes: text/select → xsd:string, number → xsd:decimal (or xsd:integer when fractionless), boolean → xsd:boolean, date → xsd:date, dateTime → xsd:dateTime. Extensions map their own property types via addRdfValueMapper. A Statement whose property type has no registered mapper — including an unregistered type — is omitted from the projection.
A Subject whose Schema cannot be loaded (for example, its Schema page was deleted) is omitted from the projection; a warning is logged for each.
Projected triples
A Subject becomes one RDF resource: its rdf:type is its Schema's neo-schema: class, and its label is always emitted as rdfs:label. Each Statement adds triples whose predicate is the property's neo-prop: IRI, one per part of a multi-part Value.
A Relation is emitted twice, so that a simple query never has to navigate reification:
| Layer | Triples |
|---|---|
| Direct | neo-subj:<source> neo-prop:<RelationType> neo-subj:<target> |
| Reified | neo-rel:<relationId> a neo:Relation, carrying neo:source, neo:target, neo:relationType, and each scalar Relation property as neo-prop:<key> |
Both are always present, including for a Relation carrying no properties: the reified node is what preserves the Relation ID. The direct predicate is the Relation type declared in the Schema, which need not match the property name, and neo:relationType's object is that same predicate IRI. Non-scalar Relation property values are dropped.
Page metadata describes the neo-page: resource, in the same named graph as the page's Subjects:
| Predicate | Emitted |
|---|---|
rdf:type neo:Page | always |
neo:pageName | when the name is not empty |
dcterms:created, dcterms:modified | as xsd:dateTime, when the stored timestamp parses |
neo:lastEditor | when not empty |
neo:category | one per category |
neo:mainSubject | when a Main Subject is set and is itself projected |
neo:hasSubject | one per projected Subject, the Main Subject included |
That list is exhaustive. Page Properties contributed by extensions through PagePropertyProvider reach the Neo4j projection only, and nothing carries a page's namespace or wiki id.
Endpoint
RDF is served per page or per Subject. Both take the same projection and format query parameters. projection selects the vocabulary: native (the default, described here) or the name of a Mapping page — see Ontology Mapping; an unknown projection returns 400. format picks the serialization, falling back to the Accept header, then to TriG; a value other than trig or turtle returns 400:
format | Accept | Content-Type | Named graph |
|---|---|---|---|
trig (default) | application/trig | application/trig; charset=utf-8 | yes |
turtle | text/turtle | text/turtle; charset=utf-8 | no (same triples, no graph wrapper) |
Responses carry Content-Disposition: inline with a filename of the page or Subject ID plus the format's extension (42.trig, 42.ttl). A response that the Accept header selected also carries Vary: Accept; passing format fixes the serialization, and those responses do not vary.
Both endpoints and the concept-URI negotiator answer one indistinguishable 404 when the page or Subject does not exist, the caller may not read it, or NeoWiki cannot describe it; a malformed Subject ID returns 400.
Page
GET /rest.php/neowiki/v0/page/{pageId}/rdf
Returns the page's projection: its page metadata and every Subject on it, in the page's named graph. Under the native projection, a page holding no Subjects projects to its page metadata alone; an ontology projection emits no page metadata at all, so the same page projects to an empty graph — a 200, not a 404.
curl 'https://wiki.example/rest.php/neowiki/v0/page/42/rdf?format=turtle'Subject
GET /rest.php/neowiki/v0/subject/{subjectId}/rdf
Returns one Subject's projection: exactly the triples the page export emits for that Subject — its description, including the native relation reification — with none of the page-metadata triples, in the hosting page's named graph. Inbound relations pointing at the Subject from elsewhere are not included.
A readable Subject whose Schema has no mapping for the requested ontology target projects to an empty graph — a 200, not a 404.
curl 'https://wiki.example/rest.php/neowiki/v0/subject/s1demo8aaaaaab5/rdf?projection=EDM'Dereferencing subject IRIs
Every Subject's neo-subj: IRI — $base/entity/{subjectId} — is a dereferenceable concept URI. A GET content-negotiates it and answers 303 See Other with an absolute Location:
Accept | Redirects to |
|---|---|
application/trig | the Subject's TriG RDF (.../subject/{id}/rdf?format=trig) |
text/turtle | the Subject's Turtle RDF (.../subject/{id}/rdf?format=turtle) |
text/html, */*, absent, anything else | the Subject's hosting page |
TriG wins when both RDF types are acceptable; the RDF redirects use the native projection.
The default HTML target is that page's Data tab (/Example_Page/subjects) opened on the Subject's row. When $wgNeoWikiDereferenceSubjectsToDataTab is set to false, the target is the wiki page itself (/Example_Page).
The negotiator is always reachable at the REST path, which needs no server configuration:
curl -H 'Accept: text/turtle' 'https://wiki.example/rest.php/neowiki/v0/entity/s1demo8aaaaaab5'To make the bare neo-subj: IRI dereference, route /entity/{id} to that REST path with an internal proxy — a plain rewrite leaves the path unchanged, so MediaWiki's REST router never matches it. The dev image ships this on Apache (mod_proxy + mod_proxy_http); its /w/ is the dev image's $wgScriptPath, which a different install replaces with its own rest.php path:
RewriteRule ^/?entity/(.+)$ http://127.0.0.1/w/rest.php/neowiki/v0/entity/$1 [P,L]This applies when $wgNeoWikiRdfBaseUri is the wiki's own host (the default); an external or institutional base URI is the operator's own routing concern.
Finding these exports
These exports are surfaced in the UI. The Data tab links to each Subject's JSON and per-projection Turtle/TriG, and the same for the whole page. Content pages that exist also emit <link rel="alternate"> autodiscovery tags (Turtle and TriG, native projection) in the HTML head, on a wiki with a graph database configured.
Bulk dump
maintenance/DumpRdf.php streams the projection of every page on the wiki to stdout as TriG, one named graph per page. Progress goes to stderr. It defaults to the native projection; --projection=<name> selects an ontology projection by its Mapping page name (see Ontology Mapping).
php maintenance/run.php NeoWiki:DumpRdf > dump.trig
php maintenance/run.php NeoWiki:DumpRdf --projection=EDM > dump-edm.trigNot covered here
- SPARQL store and live sync (the projection feeds them, but they are a separate concern).
- RDF import.
- RDFS/OWL self-description of Schemas.