Skip to main content

Paths and Containers

The Noodles.gl system uses fully qualified paths to uniquely identify operators and organize them into hierarchical containers.

Path Fundamentals​

Path Format​

All operators are identified by Unix-style absolute paths starting with /:

/operator-name              # Root level
/container/operator-name # One level deep
/container/sub/operator # Multiple levels

Path Components​

  • Root (/): All paths start from the project root
  • Containers: Directory-like groupings (e.g., /analysis/)
  • Operator Name: The final component (e.g., filter1)

Container Hierarchy​

Organization Benefits​

/                           # Project root
├── data-sources/ # Data loading container
│ ├── csv-loader
│ ├── json-loader
│ └── api-client
├── processing/ # Data processing container
│ ├── filter
│ ├── aggregator
│ └── transformer
└── visualization/ # Output container
├── map-layer
├── chart
└── dashboard/ # Nested container
├── header
└── controls

Advantages​

  • No Name Conflicts: Multiple filter operators can exist in different containers
  • Logical Grouping: Related operators stay together
  • Clear Dependencies: Path references show data flow relationships
  • Scalability: Support for complex, multi-level projects

Path Resolution​

Absolute Paths​

Start with / to specify the complete path from root:

// Reference operators by absolute path
const data = op('/data-sources/csv-loader').out.value
const filtered = op('/processing/filter').out.value
const layer = op('/visualization/map-layer').out.value

Relative Paths​

Reference operators relative to the current operator's container:

// From operator at '/processing/aggregator'

// Same container (these are equivalent)
const filtered = op('./filter').out.value
const filtered = op('filter').out.value

// Parent container
const data = op('../data-sources/csv-loader').out.value

// Nested path from current container
const chart = op('../visualization/dashboard/controls').out.value

Resolution Examples​

From operator at /processing/transformer:

ReferenceResolves ToDescription
/data-sources/csv/data-sources/csvAbsolute path
./filter/processing/filterSame container
filter/processing/filterSame container (implicit)
../visualization/map/visualization/mapParent then down
../../root-op/root-opMultiple levels up

Handle References​

Handle ID Format​

Handles combine the operator path with field information:

operatorPath.namespace.fieldName

Examples:

/data-loader.out.data                    # Data output
/processing/filter.par.threshold # Parameter input
/visualization/dashboard/chart.par.data # Nested parameter

Connection Examples​

// Connect using fully qualified node IDs and handle names in the edges array
edges: [{
id: '/data-loader.out.data->/processing/filter.par.input',
source: '/data-loader',
target: '/processing/filter',
sourceHandle: 'out.data',
targetHandle: 'par.input',
}]

Best Practices​

Naming Conventions​

// Good: Descriptive, hierarchical names
/data-sources/customer-csv
/processing/sales-filter
/visualization/revenue-chart

// Avoid: Generic names that might conflict
/data
/filter
/chart

Container Organization​

// Good: Logical grouping
/data-sources/
/processing/
/visualization/
/utilities/

// Good: Feature-based grouping
/customer-analysis/
/sales-dashboard/
/reporting/

Path References​

// Good: Use relative paths for related operators
const data = op('./data-source').out.value // Same container
const config = op('../config').par.value // Parent container

// Good: Use absolute paths for distant references
const globalConfig = op('/config/global').par.value

Performance Considerations​

  • Path Resolution: Minimal overhead for path parsing
  • Caching: Resolved paths are cached for performance
  • Memory: Qualified paths use slightly more memory than simple IDs

Troubleshooting​

Common Issues​

Path Not Found

// Check if path exists and is spelled correctly
const result = op('/processing/filtter') // Typo: 'filtter'
// Returns undefined - check console for warnings

Circular References

// Avoid circular path references
op('../container/./operator') // Redundant path segments

Container Boundaries

// Cannot navigate above root
op('../../../../../../operator') // Resolves to undefined

Debugging Tips​

  1. Console Logging: Check resolved paths in browser console
  2. Path Validation: Verify paths exist before referencing
  3. Visual Inspection: Use the node graph to trace connections
  4. Migration Logs: Check for migration warnings on project load

User Interface Features​

The system includes a breadcrumb component for navigating container hierarchies:

  • Visual Navigation: Breadcrumbs show the current path (e.g., root > processing > filters)
  • Click Navigation: Click any breadcrumb to navigate to that container level
  • Keyboard Navigation: Press u key to navigate up one container level
  • Dropdown Menus: Breadcrumbs include dropdowns showing available sub-containers

Automatic ID Generation​

The nodeId() function generates unique operator IDs within containers:

// Generate unique IDs with automatic numbering
nodeId('filter', '/processing') // → '/processing/filter'
nodeId('filter', '/processing') // → '/processing/filter-1' (if filter exists)
nodeId('filter', '/processing') // → '/processing/filter-2' (if filter-1 exists)

Display Names​

  • Node Headers: Show only the base name (e.g., filter instead of /processing/filter)
  • Path Resolution: Full paths used internally, display names used in UI
  • Container Indicators: Visual cues show operator container membership

Advanced Usage​

Dynamic Path Construction​

// Build paths programmatically
const containerName = 'processing'
const operatorName = 'filter'
const data = op(`/${containerName}/${operatorName}`).out.value

Conditional References​

// Reference different operators based on conditions
const source = condition
? op('./primary-data').out.value
: op('./fallback-data').out.value

Path Utilities​

// Path manipulation utilities
const parentPath = getParentPath('/processing/filter') // '/processing'
const baseName = getBaseName('/processing/filter') // 'filter'
const qualified = generateQualifiedPath('filter', '/processing') // '/processing/filter'

// Container relationship utilities
const isChild = isDirectChild('/container/operator', '/container') // true
const isNested = isWithinContainer('/container/sub/op', '/container') // true
const children = getDirectChildren('/container', opMap) // Array of direct child operators

This path system provides a robust foundation for organizing complex node graphs while maintaining simplicity for basic use cases.