Early developmentNot everything described here has shipped yet.

Skip to content

Graph Model ​

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.

Overview ​

Two node types and two relationship categories:

(:Page)-[:HasSubject {isMain, isDependent}]->(:Subject:SchemaName)
(:Subject)-[:PropertyName {id}]->(:Subject)

A Page node with HasSubject edges to a Museum Subject and an Attendance Subject, typed Relation edges from Attendance to Museum and from Museum to a City Subject and to a stub node, and Statement values as node properties

Page Nodes ​

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.

PropertyNeo4j TypeDescription
wiki_idstringMediaWiki Wiki ID (database name + table prefix) of the owning wiki
idintegerMediaWiki page ID (unique per wiki)
namestringFull page title, including the namespace prefix (e.g. Help:Installation)
namespaceIdintegerMediaWiki namespace ID of the page (e.g. 0 for the main namespace, 12 for Help)
creationTimedatetimeWhen the page was created
lastUpdateddatetimeWhen the page was last modified
lastEditorstringUsername of the last editor, empty where the wiki hides it
categoriesstring[]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.

Subject Nodes ​

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.

Fixed properties ​

PropertyNeo4j TypeDescription
idstringID of a Subject of this wiki, 15 characters starting with s (unique)
namestringSubject label, where it has one (see below)
wiki_idstringMediaWiki 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.

Only Subjects of this wiki are projected, so id is always the bare form, never the sourceKey:localId form of a Subject from another Source (ADR 23). See Subject format.

A Subject need not have a label (ADR 31). One with no label carries name only as its page's Main Subject, where the value is the prefixed page title and a page move keeps it current. A query that must name every Subject can fall back to the Schema label: coalesce( s.name, head( [ l IN labels( s ) WHERE l <> 'Subject' ] ) ).

Dynamic properties ​

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:

PropertyTypeNeo4j value
text, url, selectlist of strings
numberinteger or float
booleanboolean
datelist of dates
dateTimelist of datetimes
monolingualTextlist of text@language strings (['Zinema@eu']); split on the last @
relationstored 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.

Stub Subject nodes ​

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. Identify a stub by its lack of a Schema label, not by a missing name: a Subject with no label has none either.

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 HasSubject and 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.

Relationships ​

HasSubject ​

Connects a Page node to each of its Subject nodes.

PropertyNeo4j TypeDescription
isMainbooleantrue for the Main Subject, false for the page's other Subjects
isDependentbooleantrue for a Dependent Subject, false for any other Subject

A page can have at most one Main Subject and any number of other Subjects (ADR 7).

Typed Relations ​

Subject-to-Subject relationships represent Relations. The relationship type in Neo4j is the name of the property holding the Relation (e.g. Authors, Birth place). A type that is not a valid Cypher identifier must be backtick-escaped when read (-[:`Birth place`]->).

PropertyNeo4j TypeDescription
idstringRelation ID, 15 characters starting with r

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.

Relation edges to Subjects from another Source (ADR 23) are not projected yet.

Constraints ​

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).