How to use Cypher with HydraDB
Quickstart
hydradb_graph_query, hydradb_graph_collections, hydradb_graph_admin).
Authentication
Every request needs your HydraDB API key:403. Your databases are visible only to
your organization: another organization’s databases look like names that
don’t exist, and MATCH (n) RETURN n returns only your own nodes.
Endpoints
POST /byog/databases: create a database
A database groups your collections. Creating a database here makes it ready
immediately; a database created with the standard POST /databases can also
hold graph collections. Creating a name that already exists returns 409.
POST /byog/query: run Cypher
- Each collection is an isolated graph. A query runs against one collection and cannot see data in any other.
- Collections are created automatically on the first write. Reading a collection that doesn’t exist yet returns zero rows.
- Pass user data through
paramsinstead of building it into the query string. - Collection names can use letters, digits,
_and-, must start with a letter or digit, and can be up to 64 characters long.
GET /byog/collections: list collections
An unknown database returns 404.
DELETE /byog/collections: drop one collection
Drops the collection and all its data. Deleting a collection that does not
exist also succeeds.
DELETE /byog/databases: drop a database
Drops the graph collections and, if created through POST /byog/databases, the database itself. For a database created through the standard database API, the graph collections are dropped, deleted is false, and the database stays. Use the standard DELETE /databases to remove that database.
Supported Cypher
All of openCypher is supported except server-side procedures and file loading.Modeling and CRUD
- Create, read, update and delete:
CREATE,MATCH,MERGE,SET,REMOVE,DELETE/DETACH DELETE - Pattern matching,
WHEREfilters, aggregation,ORDER BY/SKIP/LIMIT UNWINDandWITHpipelines- Indexes:
CREATE INDEX FOR (n:Label) ON (n.prop) CALL { ... }subqueries
Graph traversal and exploration
- Multi-hop patterns: chain relationships in one
MATCH:MATCH (a:Person)-[:KNOWS]->(b)-[:WORKS_AT]->(c:Company) RETURN c.name. - Variable-length traversal: follow a relationship a bounded or unbounded
number of hops with
*:MATCH (a:Person {name:$n})-[:KNOWS*1..4]->(reach) RETURN DISTINCT reach.name. - Neighborhood expansion: a node’s edges and neighbors in any direction:
MATCH (p:Person {name:$n})-[r]-(nbr) RETURN type(r) AS rel, nbr.name AS neighbor. - Path finding:
shortestPathreturns the full path (nodes and edges in traversal order), not only the endpoints. - Directed, typed, filtered traversal: outgoing (
->), incoming (<-) or either (-) edges, filtered by relationship type and by node or edge properties anywhere along the walk.
Not supported
These are rejected with400 before the query runs; nothing is executed,
and retrying the same query fails the same way:
- Procedure calls:
CALL some.procedure(...), includingdb.*andapoc.*. (CALL { ... }subqueries are fine.) LOAD CSV: send data throughparamsinstead.
- Existence checks are bare pattern predicates:
MATCH (p:Person) WHERE (p)-[:KNOWS]->() RETURN p.name AS name. TheEXISTS { ... }block form and theexists()function are not accepted. shortestPathgoes in aRETURNorWITHclause (notMATCH p = …) and the traversal must be directed.
Common Cypher queries
Load the sample graph: Five people, two companies, and theKNOWS / WORKS_AT edges between them.
Result
Result
u1 doesn’t exist.
Result
Result
Result
Result
Result
Result
KNOWS edges.
Result
Result
Result
Result
Result
Result
Result
Result
ext_id.
Result
Result
Result
Result
Response format
Rows come back indata: one object per result row, keyed by your RETURN
aliases:
- Alias every column you read:
RETURN n.name AS name. An unaliased column is keyed by its expression text ("n.name"). - A write with no
RETURNsucceeds withdata: [].
- Nodes and relationships are flat objects: their properties plus the keys
in the table. A stored property with one of those names (
id,labels,relation,source_node_id,target_node_id) is hidden in the response; alias it (RETURN n.id AS my_id). idis internal: it can be reused after deletes and does not survive an export/re-import. Key your data on a property you own, such asext_idoremail.- Integers are 64-bit. Values beyond 2⁵³ lose precision in languages that parse JSON numbers as doubles; return them as strings.
Using results in your code
A minimal client
Wrap the endpoint once. Success puts rows indata; errors are wrapped in
detail.
Reading rows
Pagination
Result sets past the deployment cap are silently truncated, so page any read that could be large. A stableORDER BY keeps pages consistent:
Bulk loading
Chunk rows to stay inside the 256 KiB body cap and the 30 s write budget;MERGE on your own key makes the load re-runnable after a failure:
Handling failures
400: the message says what to fix, either your Cypher (compiler feedback is passed through) or a query that needsLIMITor an index (budget timeout). Retrying unchanged fails identically.429/500: transient; retry with backoff. Writes built onMERGE(as in Bulk loading) are safe to retry; bareCREATEbatches are not idempotent, so a retried chunk can duplicate nodes.
Errors and limits
Errors
Errors
Errors come back with
success: false and the reason in error (also
mirrored in detail):Limits and timeouts
Limits and timeouts
A query counts as a write (and gets the larger budget) when it contains
any write clause:
CREATE, MERGE, SET, DELETE, REMOVE, FOREACH.- Paginate anything potentially large:
ORDER BY … SKIP $offset LIMIT $page. Without anORDER BY, rows dropped at the result-set cap are arbitrary. - Chunk bulk imports: see Bulk loading.
- Create indexes for properties you filter on:
CREATE INDEX FOR (n:Person) ON (n.name). A slow read usually needs one.
Migrating from Neo4j or another Cypher-compatible database
Most application Cypher from Neo4j, Memgraph, or any other Cypher-compatible database ports directly. The differences you are most likely to notice:- Procedure calls (for example Neo4j’s
CALL db.*/CALL apoc.*) are not available; the equivalents are either plain Cypher or not part of the supported surface. LOAD CSVis not available; batch data in throughparams.- Internal node ids are not portable; migrate using your own key properties,
for example
UNWIND $rows AS row MERGE (n:Person {ext_id: row.ext_id}) SET n += row.
HydraGraph client. Any source you can
read nodes and relationships from works the same way. It loads nodes first,
then relationships matched on your own key. Cypher does not allow a
relationship type as a parameter, so it writes one batch per type. It copies
Person nodes and KNOWS relationships; repeat both steps for every other
label and relationship type in your graph, and compare each count:
Using BYOG without Cypher
To use your own entities and relations for a document or memory instead of the ones HydraDB extracts, pass agraph_payload on
POST /context/ingest. It is a
JSON map keyed by source id (a document_metadata id, an app_knowledge
id, or a memory id):
- Replaces extraction: HydraDB skips extraction for each keyed source;
the source is still chunked and embedded. Relations appear in
/querygraph_contexttaggedorigin: "byog", with no query-side changes. - Persists across re-ingest: re-ingesting without a
graph_payloadre-applies the stored graph; sending a new one replaces it. - Limits: at most 5,000 entities, 10,000 relations, and 500 relations per
entity; oversized payloads return
400. A key matching no source in the request is rejected with400. - Whole-graph only: there are no per-triple updates, and relations link to their best-matching chunk even when the match is weak.
graph_payload field reference
for the full shape.