Neo4j Core Schema (Graph & Postgres Reference)
Neo4j Core Schema (Graph & Postgres Reference)
Created: Tue 29 Jul 2025 06:49:39 CEST
Document Version: 1.0 - Initial documentation with timestamp
Security Classification: Internal Technical Documentation
Target Audience: Backend Developers, Database Administrators
Author: Paul Wisén
This document defines the core data schema for the Universal Object Graph System, designed for use with Neo4j. The schema is built around a property graph model of nodes and relationships.
Graph Model Overview
The system models the physical world using a graph containing several key node labels and relationship types.
- Nodes: Represent entities like object blueprints (
ObjectClass), physical items (ObjectInstance), and identifiers (PlingsIdentifier). - Relationships: Represent how these entities are connected, such as an instance being an
INSTANCE_OFa class, a component beingPART_OFanother, or an object beingINa container.
Node Reference
1. :ObjectClass Node
Represents a template or definition of an object.
| Property | Type | Description | Example |
|---|---|---|---|
name |
String |
Required. The unique name of the class. | "Mountain Bike" |
description |
String |
A detailed description of the object class. | "A bicycle designed for off-road cycling." |
version |
Integer |
The version number for the class definition. | 2 |
owner |
String |
The entity that defined or owns this class. | "ACME Inc." |
visibility |
String |
Access control for the class (“public”, “private”). | "public" |
capabilities |
List<String> |
A list of potential uses for this object type. | ["off-road", "sport"] |
anchor_key |
String |
The public cryptographic anchor key from the HD Wallet. | "xpub..." |
path_template |
String |
The schema for interpreting the HD Wallet derivation path. | "model/factory/instance" |
2. :ObjectInstance Node
Represents a specific, physical object in the real world.
| Property | Type | Description | Example |
|---|---|---|---|
statuses |
List<String> |
The current state(s) of the physical object. | ["for_sale", "broken"] |
owner |
String |
The user or organization that owns this instance. | "user:12345" |
3. :PlingsIdentifier Node
Represents a physical or digital identifier (e.g., QR code, NFC tag).
| Property | Type | Description | Example |
|---|---|---|---|
key |
String |
Required. The unique identifier value. | "plg:44-..." |
identifier_type |
String |
The medium of the identifier. | "QR" |
status |
String |
The state of the identifier itself. | "active" |
is_one_time_use |
Boolean |
Flag for OTP-style verification identifiers. | true |
4. :ScanEvent Node
Represents a single scan event of a PlingsIdentifier, creating a historical audit trail.
| Property | Type | Description | Example |
|---|---|---|---|
timestamp |
DateTime |
Required. The exact time of the scan. | 2024-07-30T10:00:00Z |
latitude |
Float |
The GPS latitude of the scan location. | 34.0522 |
longitude |
Float |
The GPS longitude of the scan location. | -118.2437 |
scanning_user |
String |
The user who performed the scan. | "user:54321" |
owner_at_time_of_scan |
String |
The recorded owner at the moment of the scan. | "user:12345" |
Relationship Reference
Core Relationships
| Start Node | Type | End Node | Description |
|---|---|---|---|
:ObjectInstance |
INSTANCE_OF |
:ObjectClass |
Links a physical item to its blueprint. |
:ObjectClass |
SUBCLASS_OF |
:ObjectClass |
Creates the class hierarchy (e.g., Mountain Bike is a subclass of Bicycle). |
:ObjectInstance |
PART_OF |
:ObjectInstance |
Defines the physical composition of complex objects (a wheel is part of a bike). |
:PlingsIdentifier |
IDENTIFIES |
:ObjectInstance |
Links an identifier to the specific object it represents. |
:ScanEvent |
SCANNED |
:PlingsIdentifier |
Records that a specific identifier was scanned in an event. |
Spatial Relationships
These relationship types model the physical world. The authoritative list of spatial predicates is maintained in the Supabase spatial_predicates table, and these predicates become Neo4j relationship types.
Dual Spatial Relationship System (from spatial_predicates table)
The spatial system uses dual predicates to separately track current and expected locations. The authoritative list is maintained in the Supabase spatial_predicates table.
Current Location Relationships (where objects are now):
| Start Node | Type | End Node | Parent Relationship | Description |
|---|---|---|---|---|
:ObjectInstance |
CURRENT_IN |
:ObjectInstance |
✅ Yes | Subject is currently inside target |
:ObjectInstance |
CURRENT_ON |
:ObjectInstance |
✅ Yes | Subject is currently on surface of target |
:ObjectInstance |
CURRENT_LEFT_OF |
:ObjectInstance |
❌ No | Subject is currently to the left of target |
:ObjectInstance |
CURRENT_RIGHT_OF |
:ObjectInstance |
❌ No | Subject is currently to the right of target |
:ObjectInstance |
CURRENT_ABOVE |
:ObjectInstance |
❌ No | Subject is currently above target |
:ObjectInstance |
CURRENT_UNDER |
:ObjectInstance |
❌ No | Subject is currently under target |
:ObjectInstance |
CURRENT_NEXT_TO |
:ObjectInstance |
❌ No | Subject is currently next to target |
:ObjectInstance |
CURRENT_ATTACHED_TO |
:ObjectInstance |
❌ No | Subject is currently attached to target |
Normal/Expected Location Relationships (where objects should be):
| Start Node | Type | End Node | Parent Relationship | Description |
|---|---|---|---|---|
:ObjectInstance |
NORMAL_IN |
:ObjectInstance |
✅ Yes | Subject should normally be inside target |
:ObjectInstance |
NORMAL_ON |
:ObjectInstance |
✅ Yes | Subject should normally be on surface of target |
:ObjectInstance |
NORMAL_LEFT_OF |
:ObjectInstance |
❌ No | Subject should normally be to the left of target |
:ObjectInstance |
NORMAL_RIGHT_OF |
:ObjectInstance |
❌ No | Subject should normally be to the right of target |
:ObjectInstance |
NORMAL_ABOVE |
:ObjectInstance |
❌ No | Subject should normally be above target |
:ObjectInstance |
NORMAL_UNDER |
:ObjectInstance |
❌ No | Subject should normally be under target |
:ObjectInstance |
NORMAL_NEXT_TO |
:ObjectInstance |
❌ No | Subject should normally be next to target |
:ObjectInstance |
NORMAL_ATTACHED_TO |
:ObjectInstance |
❌ No | Subject should normally be attached to target |
Misplacement Detection Examples:
// Box is on wrong pallet
(:Box)-[:CURRENT_ON]->(:Pallet_A)
(:Box)-[:NORMAL_ON]->(:Pallet_B)
// Keys are contained but should be hanging
(:Keys)-[:CURRENT_IN]->(:Drawer)
(:Keys)-[:NORMAL_ON]->(:KeyHook)
// Monitor on wrong side of computer
(:Monitor)-[:CURRENT_LEFT_OF]->(:Computer)
(:Monitor)-[:NORMAL_RIGHT_OF]->(:Computer)
Note: Legacy unprefixed predicates (IN, ON, LEFT_OF, etc.) are deprecated in favor of the dual system.
Functional Relationships
These relationship types describe how objects interact rather than where they are located. They come from the authoritative catalogue in functional_relationships_system.md and are stored as uppercase relationship types in Neo4j (directional pairs are stored both ways; symmetric predicates are stored once in canonical order).
| Category | Type (Predicate) | Inverse | Symmetric? | Description | Example |
|---|---|---|---|---|---|
| Physical Composition | PART_OF |
HAS_PART |
No | Component is part of a larger assembly or set | (:Wheel)-[:PART_OF]->(:Bike) |
| Compatibility | IS_FOR |
ACCEPTS |
No | Subject is designed for target | (:Remote)-[:IS_FOR]->(:TV) |
ACCEPTS |
IS_FOR |
No | Target accepts subject | (:TV)-[:ACCEPTS]->(:Remote) |
|
WORKS_WITH |
— | Yes | Designed to function together | (:Scanner)-[:WORKS_WITH]-(:Computer) |
|
| Control | CONTROLS |
CONTROLLED_BY |
No | Subject operates target | (:Remote)-[:CONTROLS]->(:TV) |
OPERATES |
OPERATED_BY |
No | Activates / runs target | (:Switch)-[:OPERATES]->(:Light) |
|
| Power | POWERS |
POWERED_BY |
No | Subject provides electrical power | (:Battery)-[:POWERS]->(:Flashlight) |
CHARGES |
CHARGED_BY |
No | Replenishes energy of target | (:Charger)-[:CHARGES]->(:Phone) |
|
| Access | UNLOCKS |
UNLOCKED_BY |
No | Grants access to target | (:Key)-[:UNLOCKS]->(:Door) |
| Pairing | PAIRS_WITH |
— | Yes | Two items form an intended pair | (:LeftGlove)-[:PAIRS_WITH]-(:RightGlove) |
| Replacement | REPLACES |
REPLACED_BY |
No | Subject serves as substitute | (:BackupBattery)-[:REPLACES]->(:MainBattery) |
Note: Functional predicates can also be created between ObjectClass nodes to act as templates. The API resolver automatically merges explicit instance-level edges with class-level edges (marked inherited = true) so that every object gains the functional context of its class hierarchy without data duplication.
The PART_OF relationship is a special case used for both physical assembly (a wheel is part of a bike) and for defining “Set Objects” where a conceptual item contains other items (a “Glove Pair Set” has a left and right glove).
Example Cypher
// Find everything that powers a given laptop (instance-level + class templates)
MATCH (l:ObjectInstance {id:$laptop})<-[:POWERS|POWERED_BY|CHARGES|CHARGED_BY]-(src)
RETURN DISTINCT src.id, type(relationships(src,l)) AS predicate;
Supabase (Postgres) Schema
Tables
- profiles
id: uuid, not nullablefull_name: text, nullableavatar_url: text, nullableupdated_at: timestamp with time zone, nullable, defaultnow()
- organizations
id: uuid, not nullable, defaultgen_random_uuid()name: text, not nullablecreated_at: timestamp with time zone, nullable, defaultnow()created_by: uuid, nullabletype: text, not nullable, default'Individual'::textdescription: text, nullable
- organization_members
organization_id: uuid, not nullableuser_id: uuid, not nullablerole: text, not nullablejoined_at: timestamp with time zone, nullable, defaultnow()
- object_instances
id: uuid, not nullableneo4j_id: text, nullablename: text, not nullabledescription: text, nullableowner_organization_id: uuid, not nullablecreated_by: uuid, not nullablecreated_at: timestamp with time zone, nullable, defaultnow()updated_at: timestamp with time zone, nullable, defaultnow()status: text, nullablemain_image_url: text, nullablelast_scanned_at: timestamp with time zone, nullablelast_scanned_by: uuid, nullable
- object_images
id: uuid, not nullableobject_instance_id: uuid, not_nullable
This file was introduced to eliminate ambiguous names like schema.md now that the project uses both Neo4j and Supabase databases.
Last Updated: Thu Jul 10 09:18:47 CEST 2025 - Standardized PlingsIdentifier relationship from POINTS_TO to IDENTIFIES for better semantic clarity