Clauses:
MATCH,OPTIONAL MATCH,WHERE,CREATE,MERGE(+ON CREATE SET/ON MATCH SET),SET(n.p = v,n = {…},n += {…},n:Label),REMOVE,DELETE,DETACH DELETE,UNWIND,WITH,RETURN,DISTINCT,ORDER BY,SKIP,LIMIT,UNION [ALL],FOREACH (x IN list | updates),LOAD CSV [WITH HEADERS] FROM 'file:///data.csv' AS row [FIELDTERMINATOR ';'](files inside the import directory only:Database::set_import_dir/DatabaseManager::set_import_dir,--import-dirfor the server, the current directory by default in the CLI; each record is a list of strings, or a map by header; an unquoted empty field isnull,""an empty string),CALL { … }subqueries (run once per row; import outer variables withCALL (a, b) { … },CALL (*) { … }or a leadingWITH a, b; they may change the graph, and a subquery withoutRETURNkeeps each row once)Procedures:
CALL db.labels() YIELD label AS l WHERE l STARTS WITH 'P', anywhere in a query (one call per row;YIELDis required there). A query that is only aCALLreturns every output, and may leave outYIELDand the parentheses (CALL db.labels; arguments then come from the parameters of the same names). Built in:db.labels(),db.relationshipTypes(),db.propertyKeys(),db.indexes(),db.constraints(),db.awaitIndexes(),dbms.procedures(),dbms.components();SHOW PROCEDURESlists them. Applications add read-only procedures in Rust withDatabase::register_procedure(orDatabaseManager::register_procedurefor every database):use graphbuff::{ProcedureSignature, Value}; db.register_procedure( ProcedureSignature::new("my.range", ["n"], ["i"]).description("0 .. n-1"), |_ctx, args| { let n = match args[0] { Value::Int(n) => n, _ => 0 }; Ok((0..n).map(|i| vec![Value::Int(i)]).collect()) }, )?;Schema:
CREATE [RANGE] INDEX [name] [IF NOT EXISTS] FOR (n:L) ON (n.p [, n.q …]),… FOR ()-[r:T]-() ON (r.p [, r.q …])(relationship indexes),CREATE INDEX ON :L(p [, q …]),DROP INDEX name [IF EXISTS],DROP INDEX ON :L(p [, q …]),SHOW INDEXES,CREATE CONSTRAINT [name] [IF NOT EXISTS] FOR (n:L) REQUIRE n.p IS UNIQUE(alsoIS NOT NULL, and(n.a, n.b) IS UNIQUE | NODE KEY; the olderON … ASSERT …form works too),DROP CONSTRAINT name [IF EXISTS],SHOW CONSTRAINTSDatabases (through a
Session):CREATE [OR REPLACE] DATABASE name [IF NOT EXISTS] [OPTIONS {…}],DROP DATABASE name [IF EXISTS],SHOW DATABASES,SHOW DATABASE name,SHOW DEFAULT DATABASE,USE name,USE name <query>Patterns: labels, inline property maps,
$parammaps, directions--><----, type alternatives[:A|B], variable length*,*n,*min..max, named pathsp = (…), pattern predicates inWHERE,shortestPath((a)-[:T*]->(b))andallShortestPaths(…)(a bidirectional breadth-first search;WHEREconditions on the path or its relationships are applied during the search, so the result is the shortest path that passes them)Expressions: arithmetic, string concatenation, comparison chains, three-valued
AND/OR/XOR/NOT,IN,STARTS WITH,ENDS WITH,CONTAINS,=~(Java-style regular expressions, whole-string match, flags such as(?i); no lookaround or backreferences),IS [NOT] NULL,IS [NOT] [NFC|NFD|NFKC|NFKD] NORMALIZED, indexing and slicing, maps, lists,CASE, list comprehensions,reduce(acc = init, x IN list | expr),all/any/none/single, parameters,EXISTS { … },COUNT { … }andCOLLECT { … }subqueries (a pattern with an optionalWHERE, or a full read-only query; they see every outer variable)Aggregates:
count,count(*),sum,avg,min,max,collect,stdev,stdevp,percentileCont,percentileDisc(withDISTINCT), implicit groupingFunctions:
id,elementId,labels,type,keys,properties,startNode,endNode,nodes,relationships,length,size,degree,head,last,tail,range,reverse,coalesce,isEmpty,toUpper/upper,toLower/lower,trim/btrim,ltrim,rtrim(each with an optional string of characters to trim),replace,substring,left,right,split,char_length,character_length,normalize(s [, NFC|NFD|NFKC|NFKD])(Unicode 17.0),toString,toStringOrNull,toInteger,toFloat,toBoolean(and their…OrNullforms),toIntegerList,toFloatList,toBooleanList,toStringList,nullIf,valueType,randomUUID,abs,sign,ceil,floor,round(alsoround(x, precision[, mode])withUP,DOWN,CEILING,FLOOR,HALF_UP(default),HALF_DOWN,HALF_EVEN, rounding the number as written:round(2.675, 2)is2.68),sqrt,exp,log,log10,sin,cos,tan,cot,asin,acos,atan,atan2,haversin,degrees,radians,isNaN,pi,e,rand,timestamp,existsapoc-style basics, built in (no plugin):apoc.coll.sum,apoc.coll.avg,apoc.coll.min,apoc.coll.max,apoc.coll.toSet,apoc.coll.sort,apoc.coll.flatten(truefor deep),apoc.coll.contains,apoc.coll.indexOf,apoc.text.join,apoc.map.merge,apoc.map.fromPairs. Otherapoc.*names returnUnsupportedTemporal values:
date,localtime,time,localdatetime,datetimeandduration, built from ISO 8601 strings (date('2015-07-21'),datetime('2015-07-21T21:40:32.142+01:00'),duration('P1Y2M10DT2H30M')), from maps (date({year: 2015, week: 30, dayOfWeek: 2}),datetime({date: d, hour: 10, timezone: '+02:00'}),duration({hours: 1.5})), from each other (date(dt)), or with no argument for the current time (one clock per statement; alsodate.statement()and friends). Fields such asd.year,d.week,t.hour,dt.offset,dt.epochMillis,dur.months,dur.minutesOfHour. Arithmetic: temporal±duration (months first, clamped to the month's end; then days; then seconds), duration±duration, duration*and/a number;duration.between(a, b),duration.inMonths/inDays/inSeconds,datetime.fromepoch(s, ns),datetime.fromepochmillis(ms). Values of one kind compare (zoned values by instant:datetime('…T01:00+01:00') = datetime('…T00:00Z')); durations only test equality. They can be stored as properties (and in lists) and indexed for equality. Time zones are UTC offsets only: named zones such as[Europe/Paris]need a time zone database and returnUnsupportedSpatial values: 2D and 3D points, Cartesian (
point({x: 1, y: 2}),point({x: 1, y: 2, z: 3})) or WGS-84 (point({longitude: 12.5, latitude: 56.1}), withheightfor 3D), or chosen withcrs: 'cartesian' | 'cartesian-3d' | 'wgs-84' | 'wgs-84-3d'orsrid: 7203 | 9157 | 4326 | 4979. A null coordinate gives null. Fieldsx,y,z,longitude,latitude,height,crs,srid.point.distance(a, b): straight line for Cartesian points, haversine metres for geographic ones (height difference added in 3D); null across coordinate systems.point.withinBBox(p, lowerLeft, upperRight), where a geographic box may cross the 180th meridian. Points test equality but do not compare with<(null);ORDER BYsorts them after paths and before temporal values. They can be stored as properties (and in lists) and indexed for equality; HTTP returns them as{"$type": "point", "crs": …, "srid": …, "x": …, "y": …}
Not yet supported (these return an Unsupported error rather than a wrong result):
procedures that change the graph, CALL { … } IN TRANSACTIONS, LOAD CSV from URLs other than local files, relationship
constraints, named time zones, date.truncate and the other temporal truncation functions,
spatial index seeks for point.distance / point.withinBBox (they filter instead), the trim(BOTH | LEADING | TRAILING 'x' FROM s) form
(use btrim / ltrim / rtrim with a second argument).
Try it on your machine: one command starts the server with the movies graph. Needs Docker.
$ docker run --rm -p 127.0.0.1:7475:7475 -p 127.0.0.1:7687:7687 ghcr.io/janvencl/graphbuff --demo moviesThen follow Getting started.