Cypher Integration

Storage participates in the same Cypher dialect used for the rest of the graph - buckets are nodes, objects are nodes, and dedicated helper functions surface URLs, sizes, and image metadata so reports can pull everything in a single query.

Bucket DDL

-- Create
CREATE BUCKET avatars
  PUBLIC = true,
  FILE_SIZE_LIMIT = 5242880,
  ALLOWED_MIME_TYPES = ["image/png", "image/jpeg"];

-- Update
ALTER BUCKET avatars SET PUBLIC = false;

-- Drop (bucket must be empty)
DROP BUCKET avatars;

-- Inspect
SHOW BUCKETS;

Matching objects

-- Every avatar over 1 MB.
MATCH (o:`storage.Object`)
WHERE o.bucket_id = "avatars" AND o.size > 1048576
RETURN o.path, file_size(o), file_mime(o)
ORDER BY o.size DESC
LIMIT 20;

Helper functions

FunctionReturnsUse
file_url(o)stringAuthenticated download URL
signed_url(o, ttl_secs)stringMint a signed URL inline
file_size(o)intByte size
file_mime(o)stringMIME type
file_etag(o)stringStrong ETag
file_hash(o)stringSHA-256 content hash
file_exists(bucket, path)boolCheap existence check
bucket_size(bucket)intSum of object sizes in a bucket
image_metadata(o)map{width, height, format, color_space}

Attaching objects to user-defined nodes

A common pattern: store the object as a node, then connect it with a typed relationship to a domain object (User, Document, Product…). RLS, sync, and triggers all see this just like any other edge.

MATCH (u:User {id: $userId})
MATCH (o:`storage.Object` {bucket_id: "avatars", path: $path})
CREATE (u)-[:AVATAR]->(o);

-- Later: fetch every user's avatar URL in one round-trip.
MATCH (u:User)-[:AVATAR]->(o:`storage.Object`)
RETURN u.name, file_url(o) AS avatar_url;