Skip to contentSkip navigation

Cypher

The read-only Cypher dialect at a glance: supported clauses, subqueries and functions, the five golden rules, and the full language reference pages.

On this page (5)

Cypher Documentation

WhisperGraph speaks a read-only dialect of Cypher, the graph query language, over HTTP. You POST a query to the Cypher API and get back columns and rows; there is no driver to install and no session to manage. If you have used Neo4j, you already know most of the language. Write clauses (CREATE, MERGE, SET, DELETE, REMOVE, FOREACH) are recognized by the parser and rejected. Parameters bind with $name through the request's parameters field, and several statements separated by ; run as one batch.

This section is the language reference. Syntax & Clauses covers every supported clause with verified examples, Functions documents the full function library, Best Practices collects the habits that separate an instant answer from a query that grinds, and the Cheat Sheet fits the whole language on one page.

The shape of a query

Almost every WhisperGraph query is anchor, traverse, return: pin a node by its indexed name, walk the edges you care about, project columns, cap the rows.

cypher · runnablegraph.whisper.securitySign in to run
MATCH (h:HOSTNAME {name: "google.com"})-[:RESOLVES_TO]->(ip:IPV4)
RETURN ip.name AS ip
LIMIT 5

On a graph with 39.5B edges, the anchor is what makes this fast. The engine starts at one indexed node and touches only connected edges. Skip the anchor and you ask for a scan over billions of nodes, which is slow at best and rejected at worst.

For anything with more than one stage, narrow before you expand. WITH ... LIMIT bounds the intermediate set so the next stage starts from a handful of nodes:

cypher · runnablegraph.whisper.securitySign in to run
MATCH (h:HOSTNAME {name: "google.com"})<-[:NAMESERVER_FOR]-(ns:HOSTNAME)
WITH ns LIMIT 3
MATCH (ns)-[:NAMESERVER_FOR]->(sibling:HOSTNAME)
RETURN ns.name AS nameserver, collect(DISTINCT sibling.name)[0..8] AS domains
LIMIT 3

The same building blocks shape results: UNWIND turns a list of indicators into one anchored lookup per element, and aggregation (count, collect) with ORDER BY ... LIMIT ranks a traversal. Both are covered with examples in Syntax & Clauses.

Labels and edge names must match the live schema exactly; the graph uses HOSTNAME (there is no Domain or FQDN label). An unknown name in an anchored pattern matches nothing, so when a query returns nothing, check CALL db.labels() and CALL db.relationshipTypes() first — both are cheap and both answer immediately. Labels carried over from other graph products (Domain, IpAddress, Certificate) are rejected with an error that names the label to use instead. The full model is in the Graph Schema.

Read coverage before band. Only known-clean — coverage: known-clean. In coverage, no malicious evidence. licenses the word "clean"; no-data — coverage: no-data. Not in coverage. This is not a verdict — nothing was looked at. means unknown, which is a different thing again; malicious-evidenced — coverage: malicious-evidenced. In coverage, with positive evidence of malice. and ambiguous — coverage: ambiguous. In coverage, and the evidence points both ways. mean there is evidence, whatever the band says. whisper.explain does not return coverage at all. Full contract: Coverage — what we looked at.

The five golden rules

  1. Anchor on an indexed name, with the label. Pin at least one node with {name: "..."}. Names are stored lowercase, so lowercase the value in your own code rather than wrapping the anchor in toLower(), which turns the lookup into a scan. Unanchored scans on billion-node labels like HOSTNAME and IPV4 do not finish.
  2. Always LIMIT. On every query, including CALL ... YIELD ... RETURN. For graph-wide totals, read the precomputed stats endpoint or CALL db.relationshipTypes() YIELD type, count instead of counting edges.
  3. Bound every branch. Narrow intermediates with WITH ... LIMIT before expanding, and put per-branch work in a bounded CALL { ... } subquery so one high-fan-out hop can't blow up the whole query. A LIMIT at the end does not bound the traversal that feeds it, and collect(DISTINCT x)[0..N] slices after collecting, so the bound goes before the fan-out.
  4. Walk edges in their stored direction, and keep variable-length walks bounded. Mail and nameserver edges point server → domain, so a domain's MX is (d)<-[:MAIL_FOR]-(mx). Edges computed at query time (ROUTES, ANNOUNCED_BY, LISTED_IN, BGP_NEIGHBOR) work inside [*1..N] when one endpoint is anchored; always write the upper bound, use BGP_NEIGHBOR (not PEERS_WITH) for peering, and filter WHERE n <> a so the walk does not report the origin as its own neighbour.
  5. Procedures first. explain(), whisper.assess(), whisper.enrich(), whisper.identify(), whisper.search(), whisper.variants(), whisper.history(), and whisper.origins() answer the hardest questions in one call, without the wide traversal a hand-written equivalent needs. Quote every argument, and YIELD the exact column names. Signatures are in Procedures.

Best Practices expands each rule into do-this-not-that pairs and the working rules behind them.

Clauses at a glance

Every clause below is documented with runnable examples in Syntax & Clauses.

ClauseWhat it does
MATCH / OPTIONAL MATCHFind graph patterns; OPTIONAL MATCH keeps rows and fills null for sparse data like WHOIS contacts.
WHEREFilter with comparisons, AND/OR/NOT/XOR, IN, IS NULL, STARTS WITH / ENDS WITH / CONTAINS, and =~ full-match regex.
RETURNProject columns; AS aliases, DISTINCT deduplicates, RETURN * returns every bound variable.
WITHPipe one stage into the next; aggregate, filter, or bound (WITH ... LIMIT) mid-query.
ORDER BY / LIMIT / SKIPSort, cap, and page results with literal numbers.
UNWINDExpand a list into rows; the batch-lookup pattern, and the replacement for a long IN list.
UNION / UNION ALLCombine branches that return the same column names; each branch can carry its own LIMIT.
CALLRun a procedure with YIELD (once per incoming row after UNWIND), or scope a bounded CALL { ... } subquery.
EXISTS { } / COUNT { }Test or count a pattern as an expression without binding it.
[x IN list WHERE ... | ...] / [(a)-->(b) | b.name]List and pattern comprehensions; build lists inline, then slice them.
CASEConditional expressions, simple and searched.
$nameParameters, bound through the request's parameters object.
;Multi-statement batching; the response becomes a results array with one outcome per statement.
EXPLAIN / PROFILEReturn the query plan (PROFILE also runs it and reports rows and execution time); confirm the anchor hits the index (NodeLookup, never a label scan).
shortestPathMinimum-hop path between two anchored nodes; requires an explicit, tight path bound.

Functions at a glance

Full signatures, examples, and return values are in Functions.

GroupFunctions
Aggregationcount, sum, avg, min, max, collect, each with DISTINCT; plus percentileCont, percentileDisc, stDev, stDevP
StringtoUpper/upper, toLower/lower, trim, ltrim, rtrim, replace, substring, split, left, right, reverse, size/length, isEmpty, toString
Numeric & trigabs, ceil/ceiling, floor, round, sign, sqrt, log, ln, log10, exp, e, pi, rand, sin, cos, tan, asin, acos, atan, atan2, degrees, radians
Collectionsize, head, last, tail, range, reverse, keys, isEmpty
Node & relationshipid (a string), elementId, label, labels, type, properties, startNode, endNode, nodes, relationships, length
Type conversiontoInteger/toInt, toFloat, toBoolean, toIntegerList, toFloatList, toStringList, toBooleanList
Date & timetimestamp, date, datetime, localdatetime, time, localtime, duration, duration.between, duration.inDays, duration.inMonths, duration.inSeconds
Geo & miscpoint, distance/point.distance, coalesce, randomUUID

Go deeper

  • Syntax & Clauses — every supported clause, subquery form, parameter binding, batching, and pattern and path syntax.
  • Functions — the full library with signatures and example results.
  • Best Practices — do this, not that; the working rules and how to design around them.
  • Cheat Sheet — the language on one page, ready to copy.
  • Cypher API — the endpoint, how to send your key in the X-API-Key header, and the response envelope.
  • Cross-cutting recipes — the patterns that keep large or repetitive jobs fast.
  • Use Cases — copy-paste recipes and runnable workflows organized by job.