ArcFlow
Company
Managed Services
Markets
  • News
  • LOG IN
  • GET STARTED

OZ brings Visual Intelligence to physical venues, a managed edge layer that lets real-world environments see, understand, and act in real time.

Talk to us

ArcFlow

  • World Models
  • Sensors

Managed Services

  • OZ VI Venue 1
  • Case Studies

Markets

  • Sports
  • Broadcasting
  • Robotics

Company

  • About
  • Technology
  • Careers
  • Contact

Ready to see it live?

Talk to the OZ team about deploying at your venues, from a single pilot match to a full regional rollout.

Schedule a deployment review

© 2026 OZ. All rights reserved.

LinkedIn
ArcFlow Docs
Start
  • Quickstart
  • Installation
  • Bindings
  • Platforms
  • Get Started
  • Cookbook
Concepts
  • World Model
  • Graph Model
  • Evidence Model
  • Observations
  • Confidence & Provenance
  • Proof Artifacts & Gates
  • SQL vs GQL
  • Graph Patterns
  • Parameters
  • Query Results
  • Persistence & WAL
  • Snapshot-Pinned Reads
  • Error Handling
  • Execution Models
  • Causal Edges
  • Adapter Discipline
  • Time Decay
  • Layers
  • 1. World Store
  • 1a. World Store · Smart Reader
  • 2. Perception Lake
  • 3. World Graph
  • 4. Query Engine
  • 5. Live Surface
  • 6. Event Bus
  • 7. Behavior Engine
  • 8. Algorithm Library
  • Virtual Computed Columns
  • Threading Model
  • Typed ID Contract
WorldCypher
  • Overview
  • Execution Options
  • Statements
  • MATCH
  • WHERE
  • RETURN
  • OPTIONAL MATCH
  • CREATE
  • SET
  • MERGE
  • DELETE
  • REMOVE
  • Composition
  • WITH
  • UNION
  • UNWIND
  • CASE
  • Schema
  • Schema Overview
  • Indexes
  • Constraints
  • Functions
  • Built-in Functions
  • Aggregations
  • Procedures
  • Shortest Path
  • EXPLAIN
  • PROFILE
  • Temporal Queriesfacet
  • Spatial Queriesfacet
  • Algorithmsfacet
  • Triggers
Capabilities
  • Live Queries
  • Vector Search
  • Trusted RAG
  • Spatial Knowledge
  • Temporal
  • Behavior Graphs
  • Graph Algorithms
  • Skills
  • CREATE SKILL
  • PROCESS NODE
  • REPROCESS EDGES
  • Sync
  • Programs
  • GPU Acceleration
  • Agent-Native
  • MCP Server
  • Event Sourcing
  • Intent Relay
  • Event Bus
Use Cases
  • Agent Tooling
  • Trusted RAG
  • Knowledge Management
  • Behavior Graphs
  • Autonomous Systems
  • Physical AI
  • Digital Twins
  • Robotics & Perception
  • Sports Analytics
  • Grounded Neural Objects
  • Fraud Detection
Walkthroughs
    Guides
  • Agent Integration
  • Building a World Model
  • Modeling a Social Graph
  • Build a RAG Pipeline
  • Using Skills
  • Behavior Graphs
  • Swarm & Multi-Agent
  • Fleet Coordination
  • Migrate from Cypher / Neo4j
  • From SQL to GQL
  • Filesystem Workspace
  • Data Quality
  • Code Intelligence
  • Scale Patterns
  • v0.7 → v0.8 Lakehouse Fast-Path
  • Tutorials
  • Knowledge Graph
  • Entity Linking
  • Vector Search
  • Graph Algorithms
  • Recipes
  • CRUD
  • Multi-MATCH
  • MERGE (Upsert)
  • Full-Text Search
  • Batch Projection
  • Multi-Source Observation
  • Sports Analytics
Operations
  • CLI
  • REPL Commands
  • Snapshot & Restore
  • Filesystem Projection
  • Plugin Management
  • Agent Governance
  • Server Modes & PG Wire
  • Persistence (ops)
  • Import & Export
  • Deployment
  • Deployment Modes
  • Daemon (UDS)
  • Why not Docker
  • Architecture
  • Engine Architecture
  • Cloud Architecture
  • Sync Protocol (Deep Dive)
  • World Graph Substrate (Preview)
Reference
  • TypeScript API
  • Glossary
  • Naming & Domain Map
  • Data Types
  • Operators
  • Error Codes
  • GQL Reference
  • Known Issues
  • Versioning
  • Licensing
  • Conformance
  • GQL Conformance
  • openCypher TCK
  • Extension Regressions
GQL Reference
    Conformance
  • Conformance Dashboard
  • openCypher TCK Results
  • Extension Regressions
  • Features
  • MATCH Basic
  • CREATE Nodes Edges
  • SET REMOVE Properties
  • DELETE Detach DELETE
  • RETURN WITH WHERE
  • Order BY Limit Skip
  • Order BY Nulls First Last
  • UNWIND
  • Aggregate Functions
  • OPTIONAL MATCH
  • Variable Length Paths
  • Label OR AND NOT Expressions
  • Label Wildcard
  • Quantified Path Sugar
  • Path Modes Walk Trail Simple Acyclic
  • Shortest Path Variants
  • IS Labeled Predicate
  • Element ID Function
  • IS Type Predicate
  • Binary Literals
  • Line Comments Solidus
  • Line Comments Minus
  • GQLSTATUS Result Codes
  • GQL Error Code Mapping
  • Transaction Control Syntax
  • SET Session
  • Conditional Execution WHEN THEN ELSE
  • RETURN NEXT Pipeline
  • Primary Key Constraint
  • Unique Constraint
  • Deterministic MERGE Via PK
  • Undirected Edge MATCH
  • Cast Type Conversion
  • GQL Directories
  • Multiple Labels Per Node
  • GQL Flagger
  • NEXT Linear Composition
  • Cardinality Function
  • INT64 BIGINT Type Names
  • FLOAT64 Double Type Names
  • Log10 Log2 Functions
  • Trim Leading Trailing Both
  • FILTER Clause
  • LET Statement
  • Group BY Explicit
  • EXCEPT SET Operations
  • INTERSECT SET Operations
  • ALL Different Predicate
  • Same Predicate
  • Property Exists Function
  • Path Variable Binding
  • USE Graph Clause
  • FOR IN List
  • Typed Temporal Literals
  • Session SET Value Params
  • Typed List Annotations
  • arcflow.cosine() function
  • arcflow.embed() function
  • arcflow.similar() procedure
  • arcflow.graphrag() procedure
  • ArcFlow Extensions
  • LIVE Queries
  • Triggered Write-Back Views
  • Evidence Algebra
  • Relationship Skills
  • AI Function Namespace
  • Graph Embedding Algorithms
  • ASOF JOIN
  • Durable Workflows
  • Incremental Z-Set Engine
  • GPU GraphBLAS
  • Triggers
  • HNSW Vector Index
  • Extensions Moat

Spatial Knowledge

Space is a first-class dimension of the world model — not a numeric column and not a join between two systems.

ArcFlow's R*-tree index makes spatial position a native query axis alongside graph relationships and temporal state. K-nearest, radius, bounding-box, and frustum queries compose with graph traversal in a single GQL statement — "find the 5 nearest warehouses that have inventory for SKU X" is one query, not two. Position updates trigger live geofence queries through incremental propagation. No approximation, no separate spatial service, no infrastructure gap between where things are and what they connect to.

-- Supply chain: spatial filter → graph traversal → property filter — one query
CALL algo.nearestNodes(order.location, 'Warehouse', 5)
  YIELD node AS wh, distance
MATCH (wh)-[:SUPPLIES]->(item:Item {sku: $sku})
WHERE item.stock > 0
RETURN wh.name, distance, item.stock
ORDER BY distance

Why R*-tree#

Most spatial indexes in the graph database world are built for the wrong problem:

Index typeDesigned forExact?
HNSW (vector similarity)Embedding nearest-neighbor — approximate by designNo
Random-walk samplingGraph connectivity estimation — misses answersNo
GIST R-tree2D geometry containmentYes
R*-tree (ArcFlow)2D/3D spatial containment, KNN, proximityYes

HNSW finds "approximately similar" embeddings. For exact containment, bounding-box overlap, and dynamic 2D/3D boundaries it produces false positives and has no structural advantage over a spatial tree. Random-walk sampling is explicitly approximate — controlled by a sampleRate parameter that trades correctness for speed.

The R*-tree uses forced reinsertion on node overflow — instead of immediately splitting a full node, it removes ~30% of entries and reinserts them. This stochastic restructuring minimises bounding-box overlap and dead space, giving faster queries than a basic R-tree on clustered data.


Spatial Primitives#

Point Types#

-- 2D cartesian (pitch, factory floor, map tile)
CREATE (p:Player {name: 'Alice', position: point({x: 34.2, y: 67.1})})
 
-- 3D cartesian (venue world space, robot arm, drone)
CREATE (r:Robot {name: 'AGV-7', position: point({x: 12.5, y: 8.3, z: 0.0})})
 
-- Geographic (WGS84)
CREATE (s:Sensor {name: 'Cam-01', location: point({latitude: 51.5074, longitude: -0.1278})})

Flat coordinate properties (x, y, z) are also indexed automatically. Point and Point3d values are stored in a SIMD-optimized columnar layout — axes are contiguous in memory, enabling AVX2 to evaluate 4 distance comparisons per instruction. This eliminates the cache-miss penalty of pointer-chasing through heap-allocated node structs.

K-Nearest Neighbor#

CALL algo.nearestNodes(point({x: 10.0, y: 10.0}), 'Sensor', 5)
  YIELD node, distance
  RETURN node.name, distance

The index is dynamic — inserts, updates, and deletes are O(log N) with no rebuild. Throughput characteristics are workload- and platform-dependent; run cargo bench against your dataset to measure on your hardware.

Radius Search#

-- All players within 20 meters
MATCH (p:Player)
WHERE distance(p.position, point({x: 10.0, y: 10.0})) < 20.0
RETURN p.name, p.position

The query compiler detects distance() predicates and pushes them into the R*-tree (SpatialIndexScan plan node) rather than scanning all nodes. A coarse grid pre-filter reduces R*-tree candidate sets by ~95% on typical datasets.

Bounding Box#

-- Entities within a rectangular zone
MATCH (e:Entity)
WHERE e.position.x >= 0.0 AND e.position.x <= 50.0
  AND e.position.y >= 0.0 AND e.position.y <= 30.0
RETURN e.name

Frustum / Visibility Queries#

Objects within a camera or sensor frustum use 6-plane containment:

-- Entities visible from a camera frustum
CALL arcflow.scene.frustumQuery(ox, oy, oz, dx, dy, dz, fovDeg, nearZ, farZ)
  YIELD node, distance
  RETURN node.name, distance
ORDER BY distance
 
-- Nearest object visible to any of N cameras
  YIELD node, distance
  RETURN node.name, distance
 
-- Line-of-sight raycast
CALL spatial.raycast(
  point({x: 0, y: 0, z: 2}),   -- origin
  point({x: 1, y: 0, z: 0}),   -- direction
  100.0                          -- max distance
) YIELD hit, distance
RETURN hit.name, distance

objectsInFrustum uses 6-plane containment over an index-narrowed candidate set.


Spatial + Graph in One Query#

The R*-tree and the graph share unified in-process memory. No serialization. No network hop between a spatial service and a graph database. Fused spatial+graph query plans execute as a DAG — the spatial prefilter and graph traversal run concurrently, and the result merge fires as soon as both branches complete. No intermediate materialization.

Supply Chain#

-- Nearest warehouses with inventory → shortest delivery route
CALL algo.nearestNodes(order.location, 'Warehouse', 5)
  YIELD node AS wh, distance AS crow_flies
MATCH (wh)-[:SUPPLIES]->(item:Item {sku: $sku})
WHERE item.stock > 0
MATCH path = shortestPath((wh)-[:ROAD*]->(dest:Depot {id: $depot}))
RETURN wh.name, item.stock, length(path) AS road_hops
ORDER BY road_hops

IoT / Fleet#

-- Vehicles within 500m, with valid license, maintained within 30 days
CALL algo.nearestNodes($zone_center, 'Vehicle', 50)
  YIELD node AS v, distance
WHERE distance < 500
MATCH (v)-[:DRIVEN_BY]->(d:Driver)-[:HOLDS]->(l:License)
WHERE l.expires > timestamp()
  AND v.last_service_date > timestamp() - 2592000000
RETURN v.plate, d.name, distance

Live Production (50fps)#

-- Offside detection: spatial filter → graph → rule check
CALL algo.nearestNodes(ball.position, 'Player', 22)
  YIELD node AS player, distance
MATCH (player)-[:PLAYS_FOR]->(team:Team {side: 'attacking'})
WHERE player.position.x > last_defender.position.x
RETURN player.name, player.position

Epidemiology#

-- People within 10m of patient zero → contact graph → 48h temporal filter
CALL algo.nearestNodes(patient_zero.location, 'Person', 1000)
  YIELD node AS contact, distance
WHERE distance < 10.0
MATCH path = (patient_zero)-[:CONTACT*1..3]->(contact)
WHERE ALL(r IN relationships(path) WHERE r.timestamp > timestamp() - 172800000)
RETURN contact.name, length(path) AS degrees, distance

Live Geofencing#

Spatial standing queries fire through incremental propagation from the nodes whose spatial properties changed — the engine does not recompute all standing queries on every mutation.

-- Named geofence: tracks all players entering zone alpha
CREATE LIVE VIEW zone_alpha_occupants AS
  MATCH (p:Player)
  WHERE p.position.x >= 30 AND p.position.x <= 70
    AND p.position.y >= 30 AND p.position.y <= 70
  RETURN p.name, p.position
 
-- Read current occupants
MATCH (row) FROM VIEW zone_alpha_occupants RETURN row.name
 
-- Anonymous live geofence: radius-based
LIVE MATCH (p:Player)
WHERE distance(p.position, point({x: 50, y: 50})) < 25.0
RETURN p.name, p.position

In TypeScript, subscribe to geofence enter/exit events via the live query API:

const lq = db.subscribe(
  "MATCH (p:Player) WHERE distance(p.position, $center) < $radius RETURN p.name, p.position",
  (event) => {
    for (const row of event.added)   console.log('entered:', row.name)
    for (const row of event.removed) console.log('left:',    row.name)
  },
  { pollIntervalMs: 20 }
)
// Stop when done
lq.cancel()

event.added contains entities that entered the zone since the last tick. event.removed contains entities that left. GeofenceEnter and GeofenceExit transitions are edge-triggered — a query that stays inside the zone does not fire on every tick, only on the boundary crossing.

Three trigger priorities control batching latency:

PriorityLatencyUse case
Immediate1-tickOffside detection, goal-line technology
Fast2–3 ticksZone entry/exit alerts
Background10+ ticksHeatmaps, session analytics

Coordinate Frame Validation#

ArcFlow validates coordinate system metadata at ingest. Mixing coordinate frames — WGS84 positions in a Z-up cartesian graph — raises a hard error rather than silently producing wrong results.

CALL db.spatialMetadata()
  YIELD crs, meters_per_unit, up_axis, handedness, calibration_version
FieldDefaultDescription
crs"cartesian"Coordinate reference system ("cartesian", "WGS84")
meters_per_unit1.0Scale — 1.0 for meters, 0.01 for centimeters
up_axis"Z"Z-up (venue/robotics default) or Y-up (DCC/game engines)
handedness"right""right" or "left"
calibration_version0Increment on venue re-calibration to invalidate stale queries

Errors raised on mismatch:

  • CrsMismatch — inserting WGS84 point into a Cartesian graph
  • UpAxisMismatch — Y-up data into a Z-up store
  • UnitScaleMismatch — centimeter data into a meter-unit store
  • StaleCalibration — data carries an older calibration version than the store

USD World Model#

ArcFlow ingests USD stage geometry in bulk and makes it immediately queryable via GQL. 500K prims load in under 1 second using Sort-Tile-Recursive spatial packing.

import { open } from '@ozinc/arcflow'
 
const db = open('./scene')
 
// Ingest USD prim hierarchy — creates graph nodes with spatial index
db.ingestUsdPrims(primRecords)
 
// Query the ingested geometry via GQL
const nearby = db.query(
  "MATCH (p:Mesh) WHERE distance(p.position, $cam) < 500 RETURN p.name, p.position",
  { cam: JSON.stringify([120.0, 45.0, 30.0]) }
)

Coordinate frame (metersPerUnit, upAxis, CRS) is validated against the store's SpatialMetadata before the first insert. A mismatch on any field raises a hard error — no partial ingestion.

Instanced geometry (chairs, seats, sensors in stadium models) shares a single spatial index in memory. 10,000 instances of the same prim geometry consume one index allocation, not 10,000. Queries into instanced geometry transform the query coordinates into prim-local space rather than rebuilding or copying the shared index.


Multi-GPU Dispatch#

Spatial queries are automatically routed to the appropriate execution lane based on a cost model that accounts for query shape, candidate count, and GPU transfer overhead:

LaneWhenCandidates
CpuLiveLive queries, time-critical≤ 500
CpuBatchAnalytics, replay, background> 500 (CPU faster than GPU transfer)
GpuLocalHigh-density spatial> 50K, fits single GPU
GpuMultiLarge-scale spatialDoesn't fit one GPU

For multi-GPU setups, nodes are partitioned across GPUs by a stable hash of node_id. Queries spanning GPU boundaries fan out to all relevant GPUs and merge results. GPUs connected via NVLink or Infinity Fabric form peer islands that share work without PCIe transfer cost.

The ArcFlow GPU Index is a pointer-free spatial index designed for direct GPU traversal. It transfers to GPU memory without transformation — no fixup, no reconstruction. Pointer-based tree structures contain virtual memory addresses that are meaningless to GPU threads; the ArcFlow GPU Index is built as a parallel representation from the ground up, eliminating this boundary entirely.


Observability#

CALL arcflow.spatial.dispatch_stats()
  YIELD lane_chosen, estimated_candidates, actual_candidates,
        prefilter_us, rtree_us, gpu_transfer_us, kernel_us, total_us
FieldDescription
lane_chosenExecution lane: CpuLive, CpuBatch, GpuLocal, GpuMulti
estimated_candidatesCandidates estimated by coarse grid prefilter
actual_candidatesCandidates after R*-tree filtering
prefilter_usCoarse grid filter time (μs)
rtree_usR*-tree traversal time (μs)
gpu_transfer_usHost→device transfer time (μs; 0 for CPU lanes)
kernel_usGPU kernel execution time (μs; 0 for CPU lanes)
total_usEnd-to-end query latency (μs)

For live spatial trigger metrics:

  YIELD query_name, node_id, predicate_type, evaluation_us, fired

Multi-Clock Sensor Fusion#

Different sensors report at different rates. ArcFlow's temporal clock system handles this natively:

CALL db.clock('camera_01', 'sensor', 33)   -- 30fps
CALL db.clock('lidar_01', 'sensor', 100)   -- 10Hz
CALL db.clock('gps_01', 'sensor', 1000)    -- 1Hz
 
MATCH (r:Robot) RETURN r.position, r.orientation, r._clock_domain

Use Cases#

Digital Twin (Venue / Building / Factory)#

CREATE (v:Venue {name: 'Stadium'})
CREATE (c:Camera {name: 'Cam-01', position: point({x: 0, y: 50, z: 15})})
CREATE (z:Zone {name: 'Pitch'})
 
-- Real-time tracking
MATCH (p:Player {jersey: 7})
SET p.position = point({x: 34.2, y: 67.1, z: 0.0})
 
-- Who's near the ball?
CALL algo.nearestNodes(ball.position, 'Player', 5)
  YIELD node, distance
RETURN node.name, distance

Warehouse Robotics#

-- Assign nearest idle robot to a pick location
CALL algo.nearestNodes($pick_location, 'Robot', 1)
  YIELD node AS nearest
SET nearest.task = 'pick'
SET nearest.target = $pick_location
 
-- Collision avoidance
MATCH (a:Robot), (b:Robot)
WHERE a <> b AND distance(a.position, b.position) < 2.0
RETURN a.name, b.name, distance(a.position, b.position)

Measuring on your hardware#

Spatial operation throughput depends on host CPU, memory bandwidth, and entity count. Measure on the hardware you'll deploy on:

# From the ozinc/arcflow repo:
cargo bench --bench spatial
OperationBehaviour
K-nearest, radius searchO(log N) on the R*-tree
Frustum / line-crossing checksIndex-narrowed candidate set
Zone occupancyIncremental, edge-triggered
Live geofence triggerIncremental, edge-triggered
USD bulk ingestBulk packing for large scenes
Spatial join (NN + multi-hop graph)Combines spatial-index narrowing with graph traversal

Compared to Other Approaches#

Richer Geometry Tooling#

Some GIS-oriented stacks have richer geometry types such as LineString, Polygon, and MultiPolygon. ArcFlow's advantage is that spatial predicates compose directly with graph traversal in one engine. If you need "find entities near X, then traverse their relationships," ArcFlow eliminates the two-system join.

Approximate spatial in graph databases#

Most graph databases reach for vector similarity indexes (HNSW) or random-walk sampling when asked to do spatial work. Both are approximate by design — they trade correctness for speed in a way that is appropriate for embedding search but wrong for geometric queries. Offside detection, collision avoidance, and delivery routing require exact answers. A false positive in proximity detection is not a search quality issue — it is a safety failure.

ROS2 / Nav2#

ArcFlow complements Nav2: Nav2 handles real-time path planning with hard latency guarantees; ArcFlow provides the persistent spatial knowledge layer — mission planning, post-incident replay, spatial-temporal reasoning. The multi-clock domain system maps directly to ROS2's multi-rate sensor model.

See Also#

  • Spatial Reference — full spatial query syntax, R*-tree, frustum, and raycast
  • Robotics & Perception — sensor fusion pipeline and ROS2 integration patterns
  • Autonomous Systems — multi-agent fleet coordination on a shared spatial world model
  • Temporal Queries — combine spatial position with AS OF for historical trajectory queries
  • Digital Twins — live spatial replica of physical environments
Try it
Open ↗⌘↵ to run
Loading engine…
← PreviousTrusted RAGNext →Temporal