← Back to Main Documentation Core Systems Index

Object Relationship Decision Guide

This guide helps you choose the right approach for connecting objects in the Plings system. Understanding these distinctions is crucial for proper data modeling and user experience.

Quick Reference

Scenario Use Relationship Example
Physical assembly PART_OF (:Wheel)-[:PART_OF]->(:Bike) Bike made of wheels, frame, saddle
Set Object PART_OF (:LeftGlove)-[:PART_OF]->(:GlovePairSet) Sold as a set, images of the whole set
Functional pairing PAIRS_WITH (:Monitor)-[:PAIRS_WITH]-(:Keyboard) Independent objects that work together
Power/control POWERS/CONTROLS (:Battery)-[:POWERS]->(:Flashlight) Battery provides power to device
User organization IN_COLLECTION (:Fork)-[:IN_COLLECTION]->(:KitchenSilverware) User groups items for convenience

Spatial Relationship Parent-Child Semantics

Defining Spatial Parents

In spatial relationships, a spatial parent is an object that directly contains or supports another object. This parent-child relationship determines both movement propagation and spatial hierarchy navigation.

Parent-Creating Relationships

These relationships establish a parent-child bond where the child object follows the parent when moved:

  • IN: Child is inside parent container
    • Example: (:Keys)-[:IN]->(:Drawer) - Keys follow drawer when moved
  • CONTAINS: Parent contains child (inverse of IN)
    • Example: (:Drawer)-[:CONTAINS]->(:Keys) - Keys follow drawer when moved
  • ON: Child rests on parent support surface
    • Example: (:Box)-[:ON]->(:Pallet) - Box follows pallet when moved
  • ATTACHED_TO: Child is physically attached to parent carrier
    • Example: (:Keys)-[:ATTACHED_TO]->(:Keyring) - Keys follow keyring when moved

Non-Parent (Positional) Relationships

These relationships describe relative positioning but don’t create parent-child bonds. Objects remain independent:

  • ABOVE/UNDER: Vertical positioning without attachment
    • Example: (:Shelf)-[:ABOVE]->(:Pallet) - Shelf doesn’t follow pallet when moved
  • LEFT_OF/RIGHT_OF: Horizontal positioning without attachment
    • Example: (:Pallet1)-[:LEFT_OF]->(:Pallet2) - Neither follows the other
  • NEXT_TO: Adjacent positioning without attachment
    • Example: (:Chair)-[:NEXT_TO]->(:Table) - Neither follows the other
  • ATTACHED_TO: Physical attachment where child object follows parent
    • Example: (:Keys)-[:ATTACHED_TO]->(:Keyring) - Keys follow keyring when moved
    • Creates parent relationship for movement and hierarchy purposes

Movement Propagation Rules

The Movement Test: “When I move this object, what else moves with it?”

Parent Relationships (Objects Follow)

// Warehouse example: Moving pallet moves boxes on it
(:Box1)-[:ON]->(:Pallet)
(:Box2)-[:ON]->(:Pallet)
(:Container)-[:IN]->(:Box1)

// Movement chain: Move Pallet → Box1 and Box2 follow → Container follows Box1

Positional Relationships (Objects Stay Independent)

// Warehouse example: Moving pallet doesn't move shelf above it
(:Shelf)-[:ABOVE]->(:Pallet)
(:Pallet2)-[:LEFT_OF]->(:Pallet)

// Independence: Move Pallet → Shelf and Pallet2 remain stationary

Spatial Hierarchy Traversal

The spatialHierarchy GraphQL field should only traverse parent relationships:

// Correct hierarchy (parent relationships only)
(:Screw)-[:IN]->(:Toolbox)-[:ON]->(:Workbench)-[:IN]->(:Workshop)
// Result: [Screw, Toolbox, Workbench, Workshop]

// Don't traverse positional relationships
(:Shelf)-[:ABOVE]->(:Workbench) // Shelf is not part of Screw's spatial hierarchy

Warehouse Pallet Example

// Parent-child relationships (objects follow parent)
(:Box1)-[:ON]->(:Pallet)
(:Box2)-[:ON]->(:Pallet)  
(:Pallet)-[:IN]->(:WarehouseZone)

// Positional relationships (objects don't follow)
(:Pallet2)-[:LEFT_OF]->(:Pallet)
(:Pallet3)-[:RIGHT_OF]->(:Pallet)
(:Shelf)-[:ABOVE]->(:Pallet)

// UI Navigation:
// - Click Pallet → See Box1, Box2 (children via parent relationships)
// - Don't automatically show Pallet2, Pallet3, Shelf (positional only)
// - Move Pallet → Box1, Box2 move with it; Pallet2, Pallet3, Shelf stay put

Set Objects vs Spatial Attachment: Key Distinction

Set Objects (PART_OF) - Logical Grouping with Physical Constraints

Purpose: Create conceptual identity and ownership grouping

  • Physical constraint: Set objects cannot have PlingsIdentifiers (QR codes, NFC tags) - only components can
  • Access method: Sets are accessed through component scanning, not direct scanning
  • Movement behavior: Sets can have spatial relationships, but components also have independent locations
  • Use case: “My Ski Gloves” as logical pair, accessed by scanning either glove
// Set object (logical grouping)
(:MySkiGloves {name: "My Ski Gloves"})<-[:PART_OF]-(:LeftSkiGlove)
(:MySkiGloves {name: "My Ski Gloves"})<-[:PART_OF]-(:RightSkiGlove)

// Set can have its own spatial relationships (storage location)
(:MySkiGloves)-[:NORMAL_IN]->(:GearBag)  // Where complete set should be stored

// Components have individual spatial relationships (current location)
(:LeftSkiGlove)-[:CURRENT_IN]->(:GearBag)
(:RightSkiGlove)-[:CURRENT_IN]->(:GearBag)

// Physical identifiers only on components (NOT on set)
(:LeftSkiGlove)-[:IDENTIFIED_BY]->(:QRCode1)  // ✅ Valid
(:RightSkiGlove)-[:IDENTIFIED_BY]->(:QRCode2)  // ✅ Valid
// (:MySkiGloves)-[:IDENTIFIED_BY]->(:QRCode)  // ❌ Invalid - sets can't have identifiers

// Scanning workflow: Scan left glove → Choose "work with set" → Move both gloves

Spatial Attachment (ATTACHED_TO) - Physical Connection

Purpose: Define physical attachment and movement behavior

  • Movement behavior: Attached objects physically follow the parent when moved
  • Use case: Keys on keyring move together as a physical unit
  • Location: Parent’s location determines attached objects’ location
// Physical attachment (movement propagation)
(:HouseKey)-[:CURRENT_ATTACHED_TO]->(:Keyring)
(:CarKey)-[:CURRENT_ATTACHED_TO]->(:Keyring)

// Keyring's location determines keys' location
(:Keyring)-[:CURRENT_ON]->(:KeyHook)

// When you move the keyring, all attached keys move with it

Combined Approach: Set Object + Spatial Attachment

“My Keys” Example: Can use BOTH approaches for different purposes

// Logical grouping (set identity)
(:MyKeysSet {name: "My Keys"})<-[:PART_OF]-(:HouseKey)
(:MyKeysSet {name: "My Keys"})<-[:PART_OF]-(:CarKey)

// Physical attachment (movement behavior)  
(:HouseKey)-[:CURRENT_ATTACHED_TO]->(:Keyring)
(:CarKey)-[:CURRENT_ATTACHED_TO]->(:Keyring)

// Keyring location
(:Keyring)-[:CURRENT_ON]->(:KeyHook)

// Result: 
// - "My Keys" set provides logical identity for personal key collection
// - ATTACHED_TO relationships ensure keys follow keyring when moved
// - Can track both set ownership and physical movement behavior

Decision Guide: PART_OF vs ATTACHED_TO

Use PART_OF (Set Object) when:

  • ✅ Need logical/conceptual grouping
  • ✅ Items sold together or have shared identity
  • ✅ Need ownership inheritance
  • ✅ Components will be accessed through scanning workflows
  • ✅ Need both set-level and component-level operations
  • ✅ Set represents a “home base” while components may be distributed

Use ATTACHED_TO (Spatial Attachment) when:

  • ✅ Need physical movement propagation
  • ✅ Objects are physically connected/clipped together
  • ✅ Child objects should follow parent when moved
  • ✅ Creating spatial hierarchy for navigation

Use BOTH when:

  • ✅ Need both logical identity AND physical movement behavior
  • ✅ Example: Personal key collection that’s also physically attached to keyring

The Three Relationship Types

1. Physical Composition (PART_OF) - “Made Of” or “Sold As Set”

Definition: Objects that are physically assembled from separable components, OR objects that are sold/conceptualized as a set with a shared identity.

Key Insight: PART_OF creates logical/conceptual grouping with ownership inheritance, not spatial movement behavior.

Two subcategories:

1a. Assembly Objects - Traditional PART_OF

Use when:

  • ✅ Components can be physically removed/replaced
  • ✅ Components have individual value (resale, repair)
  • ✅ Assembly/disassembly is a real-world action
  • ✅ Components inherit ownership until separated

Examples:

  • 🚲 Bike → wheels, frame, saddle, bell, chain
  • 💻 Laptop → battery, screen, keyboard, motherboard
  • 🏠 House → rooms, doors, windows, roof
  • ⚙️ Engine → pistons, valves, spark plugs
  • 📱 Phone → screen, battery, case, camera module

1b. Set Objects - “Sold as a Set”

Use when:

  • ✅ Items are sold together as a single product
  • ✅ You need images of the complete set
  • ✅ Set has its own identity/branding/packaging
  • ✅ Individual items are equivalent members, not functional “parts”
  • ✅ You need logical grouping for ownership, not spatial movement behavior

Important Physical Constraints:

  • No direct identifiers: Set objects cannot have PlingsIdentifiers (QR codes, NFC tags)
  • Component-based access: Sets are accessed by scanning any component, then choosing set-level operations
  • Spatial flexibility: Sets can have their own spatial relationships (home base) while components have independent locations
  • Movement options: When moving via component scan, user chooses individual component vs entire set

Examples:

  • 🧤 Ski Glove Pair Set → left glove, right glove (logical grouping)
  • 🔧 Tool Set → various tools (logical grouping, tools may be stored separately)
  • ♟️ Chess Set → board, white pieces, black pieces (complete game set)
  • 🥄 Dinnerware Set → plates, bowls, cups (dining set identity)
  • 🔑 My Keys Set → house key, car key, office key (conceptual grouping of personal keys)

Data Model:

Assembly Object Example:

(:ObjectInstance {name: "My Mountain Bike"})<-[:PART_OF]-(:ObjectInstance {name: "Front Wheel"})
(:ObjectInstance {name: "My Mountain Bike"})<-[:PART_OF]-(:ObjectInstance {name: "Rear Wheel"})
(:ObjectInstance {name: "My Mountain Bike"})<-[:PART_OF]-(:ObjectInstance {name: "Saddle"})

Set Object Example:

// Logical grouping with shared identity
(:ObjectInstance {name: "My Ski Gloves", mainImage: "glove-pair.jpg"})<-[:PART_OF]-(:ObjectInstance {name: "Left Ski Glove"})
(:ObjectInstance {name: "My Ski Gloves", mainImage: "glove-pair.jpg"})<-[:PART_OF]-(:ObjectInstance {name: "Right Ski Glove"})

// Individual gloves have their own spatial relationships
(:LeftSkiGlove)-[:CURRENT_IN]->(:GearBag)
(:RightSkiGlove)-[:CURRENT_IN]->(:GearBag)

Key Features:

  • Ownership inheritance: Components inherit parent’s ownership until instantiated separately
  • Instantiation-on-demand: Create component instances only when needed (sale, repair)
  • Digital twin support: Enables detailed maintenance and repair workflows
  • Hierarchical: Components can have their own sub-components

Set Object Movement Behavior

Empty Set Objects (No Components)

// Set with no components behaves like normal object
(:EmptyToolSet)-[:CURRENT_IN]->(:StorageRoom)

// Can be moved directly like any other object
// No special component handling needed

Set Objects with Components

// Set has components
(:SkiGloveSet)<-[:PART_OF]-(:LeftGlove)
(:SkiGloveSet)<-[:PART_OF]-(:RightGlove)

// Default behavior: Moving set moves all components
// Workflow: Scan component → Choose "work with set" → Move all components

Movement Operation Types

Set-Level Movement (affects all components):

  • Trigger: Component scan + “work with set” choice
  • Result: All components update to new spatial relationships
  • Use case: Moving complete glove pair to new location

Component-Level Movement (affects single component):

  • Trigger: Component scan + “work with component only” choice
  • Result: Only scanned component updates spatial relationship
  • Use case: Taking one glove for repairs while leaving other in set location

2. Functional Relationships - “Works With”

Definition: Objects designed to interact, complement, or work together functionally.

2a. Pairing (PAIRS_WITH) - Symmetric

Use when:

  • ✅ Two items form an intended pair
  • ✅ Both items are equivalent/interchangeable in function
  • ✅ Neither is “primary” over the other

Examples:

  • 🧤 Left gloveRight glove
  • 👟 Left shoeRight shoe
  • 🥢 Chopstick #1Chopstick #2
  • 🕶️ Sunglasses lens leftSunglasses lens right
(:ObjectInstance {name: "Left Winter Glove"})-[:PAIRS_WITH]-(:ObjectInstance {name: "Right Winter Glove"})

2b. Power Relationships (POWERS/POWERED_BY) - Directional

Use when:

  • ✅ One object provides electrical power to another
  • ✅ Energy flows from source to consumer
  • ✅ Power relationship can change (swap batteries)

Examples:

  • 🔋 BatteryFlashlight
  • 🔌 ChargerPhone
  • Power bankLaptop
  • 🚗 Car batteryCar stereo
(:ObjectInstance {name: "AA Battery"})-[:POWERS]->(:ObjectInstance {name: "TV Remote"})

2c. Control Relationships (CONTROLS/CONTROLLED_BY) - Directional

Use when:

  • ✅ One object operates or manages another
  • ✅ Control can be transferred or changed
  • ✅ Not physically part of the controlled object

Examples:

  • 📺 TV RemoteTelevision
  • 🎮 Game controllerGaming console
  • 💡 Light switchCeiling light
  • 🚗 Car keyCar
(:ObjectInstance {name: "Living Room Remote"})-[:CONTROLS]->(:ObjectInstance {name: "Samsung TV"})

3. Collections (IN_COLLECTION) - “Grouped By User”

Definition: User-defined groupings for organization and bulk operations.

Use when:

  • ✅ User wants to organize objects for convenience
  • ✅ Objects don’t necessarily work together functionally
  • ✅ Grouping is for management purposes (bulk operations)
  • ✅ Objects can belong to multiple collections
  • ✅ Collection membership changes frequently

Examples:

  • 🍴 Kitchen silverware collection → all forks, knives, spoons
  • 🎮 Gaming setup → console, games, controllers, headset
  • 📚 Work project books → research materials for specific project
  • 🔧 Workshop tools → all tools in the garage
  • 🧳 Travel essentials → items always packed for trips

Data Model:

(:Collection {name: "Kitchen Silverware", owner: "user:123"})
(:ObjectInstance {name: "Dinner Fork #1"})-[:IN_COLLECTION]->(:Collection {name: "Kitchen Silverware"})
(:ObjectInstance {name: "Steak Knife #2"})-[:IN_COLLECTION]->(:Collection {name: "Kitchen Silverware"})

Key Features:

  • Multiple membership: Objects can belong to several collections
  • User-defined: Users create and manage collections
  • Bulk operations: Move all, sell all, check status of all
  • Flexible: Add/remove items easily

Decision Tree

Use this flowchart to determine the right relationship type:

🤔 How are these objects related?

├─ Are they sold as a set OR physically assembled?
│  ├─ Sold as a set (need set images)? → Set Object (PART_OF)
│  │   └─ Example: Glove pair, tool set, chess set
│  ├─ Physically assembled (removable parts)? → Assembly (PART_OF)
│  │   └─ Example: Bike has wheels, frame, saddle
│  │
│  └─ NO ↓
│
├─ Are they designed to work together functionally?
│  ├─ YES → Use functional relationships
│  │   ├─ Equal partners? → PAIRS_WITH
│  │   │   └─ Example: Left + right gloves
│  │   ├─ Power flow? → POWERS/POWERED_BY  
│  │   │   └─ Example: Battery → flashlight
│  │   └─ Control? → CONTROLS/CONTROLLED_BY
│  │       └─ Example: Remote → TV
│  │
│  └─ NO ↓
│
└─ User organizing for convenience?
   └─ YES → Use Collections (IN_COLLECTION)
       └─ Example: Kitchen silverware set

## Spatial Relationship Decision Tree

For spatial relationships, use this additional decision tree:

🤔 How are these objects spatially related?

├─ Does one object contain or support the other? │ ├─ Object is inside another? → IN (parent relationship) │ │ └─ Example: Keys in drawer, screw in toolbox │ ├─ Object rests on surface of another? → ON (parent relationship) │ │ └─ Example: Box on pallet, laptop on desk │ └─ Object is attached to another? → ATTACHED_TO (co-dependent) │ └─ Example: Monitor attached to arm │ ├─ Are they positioned relative to each other? │ ├─ Vertical positioning? → ABOVE/UNDER (positional) │ │ └─ Example: Shelf above pallet, footrest under chair │ ├─ Horizontal positioning? → LEFT_OF/RIGHT_OF (positional) │ │ └─ Example: Pallet left of another pallet │ └─ Adjacent positioning? → NEXT_TO (positional) │ └─ Example: Chair next to table │ └─ Movement Test: “If I move object A, does object B move with it?” ├─ YES → Use parent relationship (CURRENT_IN, CURRENT_ON, CURRENT_CONTAINS) └─ NO → Use positional relationship (CURRENT_ABOVE, CURRENT_LEFT_OF, etc.)

Current vs Normal Location Decision Tree

For tracking expected vs actual locations:

🤔 What type of location information do I need to track?

├─ Where is the object right now?
│  └─ Use CURRENT_* predicates for actual current location
│      └─ Example: CURRENT_ON, CURRENT_IN, CURRENT_LEFT_OF
│
├─ Where should the object be?
│  └─ Use NORMAL_* predicates for expected/designated location
│      └─ Example: NORMAL_ON, NORMAL_IN, NORMAL_LEFT_OF
│
└─ Need to detect misplacement?
   └─ Compare CURRENT_* vs NORMAL_* relationships
       ├─ Same relationship + same target = ✅ Correct placement
       ├─ Same relationship + different target = ⚠️ Wrong location
       ├─ Different relationship + same target = ⚠️ Wrong positioning
       └─ Different relationship + different target = ❌ Completely misplaced

Real-World Examples

❓ “Pair of Gloves”

Answer: Set Object using PART_OF relationships

  • Main object: “Winter Glove Pair Set” (can have images of both gloves together)
  • Individual objects: “Left Winter Glove” and “Right Winter Glove”
  • Connected by PART_OF relationships to the set object
  • Key insight: You can attach images to the set object, not to relationships
(:ObjectInstance {name: "Winter Glove Pair Set", mainImage: "both-gloves.jpg"})
  <-[:PART_OF]-(:ObjectInstance {name: "Left Winter Glove"})
  <-[:PART_OF]-(:ObjectInstance {name: "Right Winter Glove"})

❓ “Kitchen Silverware”

Answer: Collection

  • Create collection: “Kitchen Silverware Set”
  • Multiple objects: forks, knives, spoons
  • Each connects via IN_COLLECTION relationship
  • User can perform bulk operations on entire collection

❓ “Mountain Bike”

Answer: PART_OF relationships

  • Main object: “My Mountain Bike”
  • Components: wheels, frame, saddle, chain, etc.
  • Components use PART_OF → bike relationship
  • Can instantiate individual components for repair/sale

❓ “Gaming Setup”

Answer: Mix of approaches

  • Collection: “Gaming Collection” for user organization
  • Functional: Controller CONTROLS console
  • Power: Power cable POWERS console
  • Spatial: Games are IN game shelf

❓ “Warehouse Pallet with Boxes”

Answer: Parent-child spatial relationships with current vs normal tracking

  • Current parent relationships: Boxes CURRENT_ON pallet (boxes follow pallet when moved)
  • Normal parent relationships: Boxes NORMAL_ON correct_pallet (defines expected location)
  • Positional relationships: Other pallets CURRENT_LEFT_OF/CURRENT_RIGHT_OF (don’t follow when moved)
  • Spatial hierarchy: Box → Pallet → Warehouse Zone → Building (based on current relationships)
  • UI behavior: Select pallet shows boxes on it, with misplacement indicators for boxes on wrong pallet

Misplacement Examples:

// Correct placement
(:Box1)-[:CURRENT_ON]->(:Pallet_A)
(:Box1)-[:NORMAL_ON]->(:Pallet_A)
// Status: ✅ Box1 is on correct pallet

// Wrong pallet
(:Box2)-[:CURRENT_ON]->(:Pallet_A)  
(:Box2)-[:NORMAL_ON]->(:Pallet_B)
// Status: ❌ Box2 is on wrong pallet, needs to move to Pallet_B

// Wrong relationship type
(:Box3)-[:CURRENT_IN]->(:Storage_Container)
(:Box3)-[:NORMAL_ON]->(:Pallet_A)
// Status: ❌ Box3 is stored but should be on active pallet

Common Mistakes to Avoid

❌ Wrong: Using collections for functional pairs

// DON'T do this for gloves:
(:Collection {name: "Glove Pair"})
(:LeftGlove)-[:IN_COLLECTION]->(:Collection)
(:RightGlove)-[:IN_COLLECTION]->(:Collection)

Problem: Loses the semantic meaning that they’re designed as a pair.

✅ Correct: Use functional relationships

(:LeftGlove)-[:PAIRS_WITH]-(:RightGlove)

❌ Wrong: Using PART_OF for functional relationships

// DON'T do this for battery + flashlight:
(:Battery)-[:PART_OF]->(:Flashlight)

Problem: Battery isn’t permanently assembled into flashlight.

✅ Correct: Use power relationship

(:Battery)-[:POWERS]->(:Flashlight)

❌ Wrong: Using functional relationships for user organization

// DON'T do this for organizing kitchen items:
(:Fork)-[:WORKS_WITH]->(:Knife)
(:Knife)-[:WORKS_WITH]->(:Spoon)

Problem: These don’t functionally work together; it’s just user organization.

✅ Correct: Use collections

(:Fork)-[:IN_COLLECTION]->(:KitchenSilverware)
(:Knife)-[:IN_COLLECTION]->(:KitchenSilverware)
(:Spoon)-[:IN_COLLECTION]->(:KitchenSilverware)

Implementation Guidelines

For Developers

  1. Always ask the semantic question: What does this relationship mean?
  2. Consider user workflows: How will users interact with these objects?
  3. Plan for separation: Can these objects be separated or work independently?
  4. Think about bulk operations: Does the user need to act on these as a group?

For System Architects

  1. PART_OF relationships enable digital twin and maintenance workflows
  2. Functional relationships support compatibility and pairing features
  3. Collections enable user organization and bulk operations
  4. Mixed approaches are often correct for complex scenarios

For UI Designers

  1. PART_OF: Show as assembly diagrams, component lists
  2. Functional: Show as connection indicators, compatibility badges
  3. Collections: Show as user-manageable groups, bulk action buttons
  4. Current spatial relationships: Show as current containment/support hierarchies, drill-down navigation
  5. Normal spatial relationships: Show as expected/designated locations, target indicators
  6. Misplacement indicators: Visual cues when current ≠ normal location
  7. Spatial positional relationships: Show as layout/positioning indicators, don’t imply containment
  8. Multiple types: May need different views/tabs for different relationship types
  9. Correction workflows: Bulk operations to move objects to their normal locations