Plings Core Schema (Neo4j)

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_OF a class, a component being PART_OF another, or an object being IN a 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.

Start Node Type End Node Description
:ObjectInstance IN :ObjectInstance The subject is inside the object (e.g., keys in a box).
:ObjectInstance ON :ObjectInstance The subject is on the surface of the object (e.g., laptop on a desk).
:ObjectInstance UNDER :ObjectInstance The subject is under the object.
:ObjectInstance NEXT_TO :ObjectInstance The subject is adjacent to the object.
:ObjectInstance ATTACHED_TO :ObjectInstance The subject is physically attached to the object.
:ObjectInstance LEFT_OF :ObjectInstance The subject is to the left of the object.
:ObjectInstance RIGHT_OF :ObjectInstance The subject is to the right of the object.
etc. Other predicates from the spatial system are also relationship types.

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

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

  1. profiles
    • id: uuid, not nullable
    • full_name: text, nullable
    • avatar_url: text, nullable
    • updated_at: timestamp with time zone, nullable, default now()
  2. organizations
    • id: uuid, not nullable, default gen_random_uuid()
    • name: text, not nullable
    • created_at: timestamp with time zone, nullable, default now()
    • created_by: uuid, nullable
    • type: text, not nullable, default 'Individual'::text
    • description: text, nullable
  3. organization_members
    • organization_id: uuid, not nullable
    • user_id: uuid, not nullable
    • role: text, not nullable
    • joined_at: timestamp with time zone, nullable, default now()
  4. object_instances
    • id: uuid, not nullable
    • neo4j_id: text, nullable
    • name: text, not nullable
    • description: text, nullable
    • owner_organization_id: uuid, not nullable
    • created_by: uuid, not nullable
    • created_at: timestamp with time zone, nullable, default now()
    • updated_at: timestamp with time zone, nullable, default now()
    • status: text, nullable
    • main_image_url: text, nullable
    • last_scanned_at: timestamp with time zone, nullable
    • last_scanned_by: uuid, nullable
  5. object_images
    • id: uuid, not nullable
    • object_instance_id: uuid, not nullable
    • storage_path: text, not nullable
    • public_url: text, not nullable
    • is_main: boolean, not nullable, default false
    • uploaded_by: uuid, not nullable
    • uploaded_at: timestamp with time zone, nullable, default now()
    • file_size: integer, nullable
    • content_type: text, nullable
  6. functional_predicates
    • key: text, not nullable
    • category: text, nullable
    • description: text, nullable
    • inverse_key: text, nullable
    • is_symmetric: boolean, not nullable, default false
    • icon_name: text, nullable
    • deprecated: boolean, not nullable, default false
    • created_at: timestamp with time zone, nullable, default now()
  7. spatial_predicates
    • key: text, not nullable
    • axis: text, nullable
    • description: text, nullable
    • inverse_key: text, nullable
    • is_symmetric: boolean, not nullable, default false
    • icon_name: text, nullable
    • deprecated: boolean, not nullable, default false
    • created_at: timestamp with time zone, nullable, default now()

RLS policies enforce per-organisation isolation.