Problem
When a Subject is removed from its page (or its page is deleted) while still referenced by another Subject, the Neo4j projection reduces it to a stub rather than deleting it, keeping the incoming relation valid (#1080). But when two or more subjects that reference each other are removed in the same savePage/deletePage, the removals cascade: stubbing one drops the relation that made another "referenced", so a subject can end up as a stub with no remaining incoming reference — an orphan stub.
Example (A -[rel]-> B and B -[rel]-> A, both removed in one save):
- Both are classified as referenced (each has an incoming relation from the other).
- Reducing each to a stub deletes its outgoing relations, so after both are processed each has lost the relation that referenced it.
- Result: two orphan stub nodes —
:Subject nodes with only id + wiki_id, no name, no schema label, no HasSubject edge, and no incoming relation.
Path: Neo4jProjectionStore::removeSubjects() / reduceSubjectToStub().
Impact
Harmless in the supported readers, and self-healing:
Neo4jSubjectLabelLookup filters on name + schema label — a stub has neither, so it never surfaces.
Neo4jPageIdentifiersLookup traverses :HasSubject — a stub has none.
RebuildGraphDatabases.php rebuilds the projection from scratch and normalises them away.
But a raw MATCH (n:Subject) via nw.query (Lua), the Cypher REST API, or {{#cypher_raw}} does enumerate orphan stubs (they keep the :Subject label). No stale data leaks — a stub carries only id + wiki_id — so this is phantom-node / count-inflation, not data exposure.
Robust fix
Cascade-sweep newly-orphaned stubs at the end of the write transaction: after the removals, delete any :Subject node left with no HasSubject edge and no incoming non-HasSubject relation (i.e. unreachable).
Notes / history
Low priority: NeoWiki is not in production, the graph is a rebuildable projection, and the stubs are invisible to the structured readers.
Problem
When a Subject is removed from its page (or its page is deleted) while still referenced by another Subject, the Neo4j projection reduces it to a stub rather than deleting it, keeping the incoming relation valid (#1080). But when two or more subjects that reference each other are removed in the same
savePage/deletePage, the removals cascade: stubbing one drops the relation that made another "referenced", so a subject can end up as a stub with no remaining incoming reference — an orphan stub.Example (
A -[rel]-> BandB -[rel]-> A, both removed in one save)::Subjectnodes with onlyid+wiki_id, noname, no schema label, noHasSubjectedge, and no incoming relation.Path:
Neo4jProjectionStore::removeSubjects()/reduceSubjectToStub().Impact
Harmless in the supported readers, and self-healing:
Neo4jSubjectLabelLookupfilters onname+ schema label — a stub has neither, so it never surfaces.Neo4jPageIdentifiersLookuptraverses:HasSubject— a stub has none.RebuildGraphDatabases.phprebuilds the projection from scratch and normalises them away.But a raw
MATCH (n:Subject)vianw.query(Lua), the Cypher REST API, or{{#cypher_raw}}does enumerate orphan stubs (they keep the:Subjectlabel). No stale data leaks — a stub carries onlyid+wiki_id— so this is phantom-node / count-inflation, not data exposure.Robust fix
Cascade-sweep newly-orphaned stubs at the end of the write transaction: after the removals, delete any
:Subjectnode left with noHasSubjectedge and no incoming non-HasSubjectrelation (i.e. unreachable).Notes / history
Low priority: NeoWiki is not in production, the graph is a rebuildable projection, and the stubs are invisible to the structured readers.