Skip to content
Cypher
Skip navigation
Cypher
View as Markdown

Functions

The built-in Cypher function library: aggregation, string, numeric, collection, node, conversion, date and geospatial — with return values.

Published Last updated

On this page (11)

Functions Documentation

WhisperGraph Cypher ships a function library you use inside RETURN, WITH, and WHERE. The tables below give the call and the value it returns. For the clauses that hold these functions, see Syntax & Clauses; for the CALL procedures (explain, whisper.assess, whisper.variants, whisper.history, whisper.origins), see Procedures.

An unknown function name is an error, never a null: RETURN notAFunction(1) answers 400 query-error with Unknown function: notAFunction and points you at CALL db.functions(). explain is a procedure with no function form, so call it with CALL, not inside an expression.

Aggregation

Aggregations collapse rows; any non-aggregated column in the same RETURN or WITH becomes a grouping key. They are planned rather than dispatched, so CALL db.functions() does not list them.

FunctionExampleResult
countcount(*), count(c)row count
count(DISTINCT ...)count(DISTINCT ip)distinct count
sumsum(x) over [1,2,3,4]10.0
avgavg(x) over [1,2,3,4]2.5
min / maxmin(x) / max(x) over [5,2,8]2 / 8
percentileContpercentileCont(x, 0.5) over [1,2,3,4]2.5 (interpolated; 0.5 is the median)
percentileDiscpercentileDisc(x, 0.5) over [1,2,3,4]2.0 (an actual value from the set)
stDevstDev(x) over [1,2,3,4]1.29… (sample standard deviation)
stDevPstDevP(x) over [1,2,3,4]1.118… (population standard deviation)
collectcollect(c.name)a list
collect(DISTINCT ...)collect(DISTINCT x) over [1,1,2][1,2]

Two habits keep aggregations cheap. collect(DISTINCT x)[0..N] slices the list after it is built, so the whole fan-out is collected first: bound the input with WITH x LIMIT n before you collect. And a grouped result is as large as the number of distinct groups, which a trailing LIMIT does not shrink: group on a coarser key (country rather than city, ASN rather than prefix, category rather than feed), or bound the input before you aggregate.

String

FunctionExampleResult
toUpper / uppertoUpper("abc")ABC
toLower / lowertoLower("ABC")abc
trim / ltrim / rtrimtrim(" hi ")hi
replacereplace("foobar","bar","baz")foobaz
substringsubstring("hello",1,3) / substring("hello",2)ell / llo
splitsplit("a,b,c",",")["a","b","c"]
left / rightleft("hello",2) / right("hello",2)he / lo
reversereverse("abc")cba
size / lengthsize("abc")3 (string length)
isEmptyisEmpty("")true
toStringtoString(123)123

String concatenation uses +. Lowercase an anchor value in your own code, not with toLower() in the query: names are stored lowercase, and wrapping the anchor in a function turns an indexed lookup into a scan.

Numeric

FunctionExampleResult
absabs(-5)5
ceil / ceiling / floorceil(4.2) / floor(4.8)5.0 / 4.0
roundround(4.5)5 (an integer)
signsign(-3)-1
sqrtsqrt(16)4.0
log / ln / log10 / explog10(1000) / ln(e())3.0 / 1.0
randrand()a value in [0,1)
e / pipi()π

Arithmetic operators: +, -, *, /, %, ^ (exponent; 2 ^ 3 is 8.0).

Trigonometric

FunctionExampleResult
sin / cos / tancos(0)1.0
asin / acos / atan / atan2atan2(0,1)0.0
degreesdegrees(pi())180.0
radiansradians(180)π

Collection

FunctionExampleResult
sizesize([1,2,3])3
head / lasthead([10,20,30])10
tailtail([10,20,30])[20,30]
rangerange(1,5) / range(0,10,5)[1,2,3,4,5] / [0,5,10]
reversereverse([1,2,3])[3,2,1]
keyskeys(node), keys(rel)property keys (keys(r) on a RESOLVES_TO edge gives source, inferred)
isEmptyisEmpty([])true

List comprehensions and pattern comprehensions build lists inline; both are covered in Syntax & Clauses.

Node and relationship

FunctionExampleResult
idid(n)the node id, as a string ("906258972")
elementIdelementId(n)the qualified form ("4:whisper:906258972")
label / labelslabel(n) / labels(n)"HOSTNAME" / ["HOSTNAME"]
typetype(r)RESOLVES_TO
propertiesproperties(n)a property map
startNode / endNodestartNode(r).namea node
nodes / relationshipssize(nodes(p))node count
lengthlength(p)path length (hop count)
cypher · runnablegraph.whisper.securitySign in to run
MATCH (h:HOSTNAME {name: "google.com"})
RETURN h.name, id(h) AS id, elementId(h) AS elementId
LIMIT 1

Ids are strings, so compare them with =: WHERE id(a) = id(b). An ordering comparison such as WHERE id(n) > 0 compares a string with a number and matches nothing. Never look a node up by id across the whole graph (MATCH (n) WHERE id(n) = "…"), which is an unanchored scan. Anchor on name instead; on URL the name is the kit path. Adding the label does not help: MATCH (u:URL {id: "…"}) still scans every node of the label, and on URL the same id can name a different kit on the next call.

Type conversion

FunctionExampleResult
toInteger / toInttoInteger("42")42
toFloattoFloat("3.14")3.14
toBooleantoBoolean("true")true
toIntegerList / toFloatList / toStringList / toBooleanListtoIntegerList(["1","2"])[1,2]

Input that cannot be parsed yields null rather than an error: toInteger("abc") and toFloat("abc") both return null.

Date and time

FunctionExampleResult
timestamptimestamp()epoch millis
datedate()today's date, YYYY-MM-DD
datetime / localdatetimedatetime()an ISO-8601 timestamp
time / localtimetime()a time of day
durationduration("P1D"){period: "P1D", duration: "PT0S"}
duration.betweenduration.between(date("2020-01-01"), date("2020-03-01")){period: "P2M", duration: "PT0S"}
duration.inDays / duration.inMonths / duration.inSecondsduration.inDays(date("2020-01-01"), date("2020-03-01")){period: "P60D", duration: "PT0S"}

Geospatial and misc

FunctionExampleResult
pointpoint({x: 1.0, y: 2.0}){latitude: 2.0, longitude: 1.0}
distance / point.distancedistance(point({x: 0, y: 0}), point({x: 3, y: 4}))555811.94…
coalescecoalesce(a.missing, "default")default
randomUUIDrandomUUID()a UUID string

Points are geodesic, not Cartesian: x is longitude and y is latitude, and distance() returns metres along the globe, which is why the example above is not 5. distance and point.distance behave identically.

Functions that introspection leaves out

CALL db.functions() is the right way to check a name, but a few functions run even though the listing omits them: ln, localtime, duration.between, duration.inDays, duration.inMonths, duration.inSeconds, point.distance, and whisper.variants, which as a function returns the variant list directly (RETURN whisper.variants("google.com")[0..3]). An omission from the listing is not a rejection; only the Unknown function error is. The aggregation functions above are absent from the listing for the same reason: they are planned, not dispatched.

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. Full contract: Coverage — what we looked at.

Reading threat properties off a node

You do not need a function to read a verdict — the threat posture lives directly on the node. An IPV4 node carries threatScore, threatLevel, isThreat, isTor, and isAnonymizer, so a single anchored read gives you the whole posture with no extra hops.

cypher · runnablegraph.whisper.securitySign in to run
MATCH (ip:IPV4 {name: "185.220.101.1"})
RETURN ip.name AS ip, ip.threatScore AS score, ip.threatLevel AS level,
       ip.isThreat AS isThreat, ip.isTor AS isTor, ip.isAnonymizer AS isAnonymizer
LIMIT 1

Name the properties you read. A whole-node projection (RETURN ip, keys(ip), properties(ip)) may leave the reconciled verdict fields (verdictLevel, verdictScore, verdictCoverage, and their siblings) out for speed, and the response then carries a projection-verdict-omitted advisory in its top-level advisories[] array. Either project the property you need (RETURN ip.verdictLevel) or send projectionFull: true in the request body to get the full verdict surface back.

For the scored reasoning behind a verdict — the feeds, weights, and factors — call explain(). See explain() — Threat Verdicts.