| title | Graph Model |
|---|---|
| order | 6 |
NeoWiki projects its data into a Neo4j graph for querying (ADR 3). The graph is a secondary store; the source of truth is the MediaWiki revision slots it is built from (ADR 4).
For definitions of domain terms like Subject, Statement, and Schema, see the Glossary.
Two node types and two relationship categories:
(:Page)-[:HasSubject {isMain}]->(:Subject:SchemaName)
(:Subject)-[:RelationType {id, ...}]->(:Subject)
Every page is projected as a :Page node when it is saved and by
RebuildGraphDatabases.php, whether or not it holds Subjects. A
failed projection leaves the page with no node, or with one holding an earlier revision's data.
A shared graph can hold pages from multiple wikis (a wiki farm), and MediaWiki page ids are unique only within a wiki,
so a page is identified by the (wiki_id, id) pair, not id alone (ADR 22).
A single-wiki install has just one wiki_id.
| Property | Neo4j Type | Description |
|---|---|---|
wiki_id |
string | MediaWiki Wiki ID (database name + table prefix) of the owning wiki |
id |
integer | MediaWiki page ID (unique per wiki) |
name |
string | Full page title, including the namespace prefix (e.g. Help:Installation) |
namespaceId |
integer | MediaWiki namespace ID of the page (e.g. 0 for the main namespace, 12 for Help) |
creationTime |
datetime | When the page was created |
lastUpdated |
datetime | When the page was last modified |
lastEditor |
string | Username of the last editor |
categories |
string[] | MediaWiki categories the page belongs to |
Built-in namespaces have the same ID on every wiki, so namespaceId filters consistently across a shared graph.
Custom namespaces ($wgExtraNamespaces) get per-wiki IDs whose meaning can differ between wikis; pair namespaceId
with wiki_id to filter those unambiguously.
Each Subject stored on a page gets a :Subject node. Subject nodes carry two labels: Subject and the name of their
Schema (e.g. :Subject:Person, :Subject:Company). The Schema label changes if a Subject's type changes.
| Property | Neo4j Type | Description |
|---|---|---|
id |
string | Subject ID, 15 characters starting with s (unique) |
name |
string | Subject label |
wiki_id |
string | MediaWiki Wiki ID of the wiki that owns the Subject |
Unlike page ids, Subject ids are globally unique nanoids (ADR 14), so a Subject's
identity is its id alone. The wiki_id is carried only for per-wiki query filtering in a shared graph.
Each non-relation Statement becomes a node property keyed by its Property Name. A number Statement with Property
Name "Founded at" and value 2019 becomes the node property Founded at: 2019. A key that is not a valid Cypher
identifier must be backtick-escaped when read (s.`Founded at`).
The PropertyType determines the value's Neo4j type:
| PropertyType | Neo4j value |
|---|---|
text, url, select |
list of strings |
number |
integer or float |
boolean |
boolean |
date |
list of dates |
dateTime |
list of datetimes |
relation |
stored as a relationship, not a node property |
A Property Name that collides with a fixed property (id, name, wiki_id) does not override it: the fixed value
wins and the Statement's value is not projected.
Values a PropertyType cannot represent are dropped from the projection: a property whose type has no registered
PropertyType produces no node property, and non-ISO 8601 date/dateTime parts are omitted from the stored value.
The revision slot stays authoritative.
A Subject node can exist as a stub: a node stripped down to only its id and wiki_id properties and the
Subject label — no name, no Schema label, and no Statement-derived properties. A stub keeps a Subject's identity
available for incoming relations while carrying none of its data.
Stubs arise in two ways:
- Referenced but removed. When a Subject is removed from its page (or its page is deleted) but other Subjects
still hold relations to it, its node is reduced to a stub rather than deleted, and its
HasSubjectand outgoing relationships are removed. This keeps the incoming references valid. - Referenced but not yet created. When a Relation targets a Subject that does not exist yet, a stub target node is created so the relationship can be stored.
When the real Subject is later saved, its node is upgraded in place: the stub gains its properties and Schema label, and no duplicate node is created.
A write that strips a stub of its last incoming relation deletes the stub, whether that write removes the referring Subject, drops the Relation, or retargets it. Removing a set of Subjects referenced only by each other leaves none of them behind.
Connects a Page node to each of its Subject nodes.
| Property | Neo4j Type | Description |
|---|---|---|
isMain |
boolean | true for the Main Subject, false for Child Subjects |
A page can have at most one Main Subject and any number of Child Subjects (ADR 7).
Subject-to-Subject relationships represent Relations. The relationship type in Neo4j is the Relation Type defined in
the Property Definition (e.g. Has author, Has product). Names that are not valid Cypher identifiers are
backtick-escaped.
| Property | Neo4j Type | Description |
|---|---|---|
id |
string | Relation ID, 15 characters starting with r |
| (additional) | scalar | Any properties from the Relation's property map |
When a Subject is removed while other Subjects still reference it, its node is kept as a stub for as long as those references last.
Two node uniqueness constraints apply: (wiki_id, id) on :Page nodes
(ADR 22) and id on :Subject nodes. Both are created by update.php and
by RebuildGraphDatabases.php, not by the incremental per-edit projection.
Relation (edge) id values carry no uniqueness constraint
(#351).
- ADR 3: Neo4j as Graph Database
- ADR 4: Use Dedicated Slot — primary storage in MediaWiki revision slots
- ADR 13: Restrict Neo4j Access — backend-only access to Neo4j
- ADR 22: Multi-wiki Graph Node Identity — per-wiki page identity in a shared graph
- Subject Format — JSON format for Subject data in revision slots
- Schema Format — JSON format for Schema definitions