| ← 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
- Example:
CONTAINS: Parent contains child (inverse of IN)- Example:
(:Drawer)-[:CONTAINS]->(:Keys)- Keys follow drawer when moved
- Example:
ON: Child rests on parent support surface- Example:
(:Box)-[:ON]->(:Pallet)- Box follows pallet when moved
- Example:
ATTACHED_TO: Child is physically attached to parent carrier- Example:
(:Keys)-[:ATTACHED_TO]->(:Keyring)- Keys follow keyring when moved
- Example:
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
- Example:
LEFT_OF/RIGHT_OF: Horizontal positioning without attachment- Example:
(:Pallet1)-[:LEFT_OF]->(:Pallet2)- Neither follows the other
- Example:
NEXT_TO: Adjacent positioning without attachment- Example:
(:Chair)-[:NEXT_TO]->(:Table)- Neither follows the other
- Example:
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
- Example:
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 glove ↔ Right glove
- 👟 Left shoe ↔ Right shoe
- 🥢 Chopstick #1 ↔ Chopstick #2
- 🕶️ Sunglasses lens left ↔ Sunglasses 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:
- 🔋 Battery → Flashlight
- 🔌 Charger → Phone
- ⚡ Power bank → Laptop
- 🚗 Car battery → Car 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 Remote → Television
- 🎮 Game controller → Gaming console
- 💡 Light switch → Ceiling light
- 🚗 Car key → Car
(: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_OFrelationships 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_COLLECTIONrelationship - 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
CONTROLSconsole - Power: Power cable
POWERSconsole - Spatial: Games are
INgame shelf
❓ “Warehouse Pallet with Boxes”
Answer: Parent-child spatial relationships with current vs normal tracking
- Current parent relationships: Boxes
CURRENT_ONpallet (boxes follow pallet when moved) - Normal parent relationships: Boxes
NORMAL_ONcorrect_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
- Always ask the semantic question: What does this relationship mean?
- Consider user workflows: How will users interact with these objects?
- Plan for separation: Can these objects be separated or work independently?
- Think about bulk operations: Does the user need to act on these as a group?
For System Architects
- PART_OF relationships enable digital twin and maintenance workflows
- Functional relationships support compatibility and pairing features
- Collections enable user organization and bulk operations
- Mixed approaches are often correct for complex scenarios
For UI Designers
- PART_OF: Show as assembly diagrams, component lists
- Functional: Show as connection indicators, compatibility badges
- Collections: Show as user-manageable groups, bulk action buttons
- Current spatial relationships: Show as current containment/support hierarchies, drill-down navigation
- Normal spatial relationships: Show as expected/designated locations, target indicators
- Misplacement indicators: Visual cues when current ≠ normal location
- Spatial positional relationships: Show as layout/positioning indicators, don’t imply containment
- Multiple types: May need different views/tabs for different relationship types
- Correction workflows: Bulk operations to move objects to their normal locations
Related Documentation
- Functional Relationships System - Detailed functional relationship types
- Class System - Overall object model and
PART_OFexamples - Batch and Collection Management - Collection implementation details
- Ownership Handling - How ownership works with
PART_OFrelationships