Features

Graphs

A graph consists of a set of vertices and edges, where each individual node and edge possesses a map of properties. A vertex is the basic object of a graph, that can exist independently of everything else in the graph. An edge creates a directed connection between two vertices.

Create a Graph

To create a graph, use the create_graph function, located in the ag_catalog namespace.

create_graph()

Syntax: create_graph(graph_name);

Returns:

void

Arguments:

Name

Description

graph_name

Name of the graph to be created

Considerations

Example:

SELECT * FROM ag_catalog.create_graph('graph_name');

Delete a Graph

To delete a graph, use the drop_graph function, located in the ag_catalog namespace.

drop_graph()

Syntax: drop_graph(graph_name, cascade);

Returns:

void

Arguments:

Name

Description

graph_name

Name of the graph to be created

cascade

A boolean that will not drop the graph if any data remains in the graph.

Considerations:

Example:

SELECT * FROM ag_catalog.drop_graph('graph_name', true);

A Cypher Graph vs A Postgres Namespace

Cypher uses a Postgres namespace for every individual graph. It is recommended that no DML or DDL commands are executed in the namespace that is reserved for the graph.

Data Types - An Introduction to agtype

AGE uses a custom data type called agtype, which is the only data type returned by AGE. Agtype is a superset of Json and a custom implementation of JsonB.

Simple Data Types

Null

In Cypher, null is used to represent missing or undefined values. Conceptually, null means 'a missing unknown value' and it is treated somewhat differently from other values. For example getting a property from a vertex that does not have said property produces null. Most expressions that take null as input will produce null. This includes boolean expressions that are used as predicates in the WHERE clause. In this case, anything that is not true is interpreted as being false. null is not equal to null. Not knowing two values does not imply that they are the same value. So the expression null = null yields null and not true.

Input/Output Format

Query

SELECT *FROM cypher('graph_name', $$    RETURN NULL $$) AS (null_result agtype);

A null will appear as an empty space.

Result:

null_result

(1 row)

Agtype NULL vs Postgres NULL

Integer

The integer type stores whole numbers, i.e. numbers without fractional components. Integer data type is a 64-bit field that stores values between -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807. Attempts to store values outside of the allowed range will result in an error.

The type integer is the common choice, as it offers the best balance between range, storage size, and performance. The smallint type is generally only used if disk space is at a premium. The bigint type is designed to be used when the range of the integer type is insufficient.

Input/Output Format

Query

SELECT *FROM cypher('graph_name', $$    RETURN 1 $$) AS (int_result agtype);

Result:

int_result

1

(1 row)

Float

The data type float is an inexact, variable-precision numeric type, conforming to the IEEE-754 Standard.

Inexact means that some values cannot be converted exactly to the internal format and are stored as approximations, so that storing and retrieving a value might show slight discrepancies. Managing these errors and how they propagate through calculations is the subject of an entire branch of mathematics and computer science and will not be discussed here, except for the following points:

Values that are too large or too small will cause an error. Rounding might take place if the precision of an input number is too high. Numbers too close to zero that are not representable as distinct from zero will cause an underflow error.

In addition to ordinary numeric values, the floating-point types have several special values:

These represent the IEEE 754 special values “infinity”, “negative infinity”, and “not-a-number”, respectively. When writing these values as constants in a Cypher command, you must put quotes around them and typecast them, for example SET x.float_value = '-Infinity'::float. On input, these strings are recognized in a case-insensitive manner.

Note IEEE754 specifies that NaN should not compare equal to any other floating-point value (including NaN). However, in order to allow floats to be sorted correctly, AGE evaluates ‘NaN’::float = ‘NaN’:float to true. See the section Comparability and Equality for more details.

Input/Output Format:

To use a float, denote a decimal value.

Query

SELECT *FROM cypher('graph_name', $$    RETURN 1.0 $$) AS (float_result agtype);

Result:

float_result

1.0

(1 row)

Numeric

The type numeric can store numbers with a very large number of digits. It is especially recommended for storing monetary amounts and other quantities where exactness is required. Calculations with numeric values yield exact results where possible, e.g., addition, subtraction, multiplication. However, calculations on numeric values are very slow compared to the integer types, or to the floating-point type.

We use the following terms below: The precision of a numeric is the total count of significant digits in the whole number, that is, the number of digits to both sides of the decimal point. The scale of a numeric is the count of decimal digits in the fractional part, to the right of the decimal point. So the number 23.5141 has a precision of 6 and a scale of 4. Integers can be considered to have a scale of zero.

Without any precision or scale creates a column in which numeric values of any precision and scale can be stored, up to the implementation limit on precision. A column of this kind will not coerce input values to any particular scale, whereas numeric columns with a declared scale will coerce input values to that scale. (The SQL standard requires a default scale of 0, i.e., coercion to integer precision. We find this a bit useless. If you're concerned about portability, always specify the precision and scale explicitly.)

Note The maximum allowed precision when explicitly specified in the type declaration is 1000; NUMERIC without a specified precision is subject to the limits described in Table 8.2.

If the scale of a value to be stored is greater than the declared scale of the column, the system will round the value to the specified number of fractional digits. Then, if the number of digits to the left of the decimal point exceeds the declared precision minus the declared scale, an error is raised.

Numeric values are physically stored without any extra leading or trailing zeroes. Thus, the declared precision and scale of a column are maximums, not fixed allocations. (In this sense the numeric type is more akin to varchar(n) than to char(n).) The actual storage requirement is two bytes for each group of four decimal digits, plus three to eight bytes overhead.

In addition to ordinary numeric values, the numeric type allows the special value NaN, meaning “not-a-number”. Any operation on NaN yields another NaN. When writing this value as a constant in an SQL command, you must put quotes around it, for example UPDATE table SET x = 'NaN'. On input, the string NaN is recognized in a case-insensitive manner.

Note In most implementations of the "not-a-number" concept, NaN is not considered equal to any other numeric value (including NaN). However, in order to allow floats to be sorted correctly, AGE evaluates ‘NaN’::numeric = ‘NaN’:numeric to true. See the section Comparability and Equality for more details.

When rounding values, the numeric type rounds ties away from zero, while (on most machines) the real and double precision types round ties to the nearest even number. For example:

Input/Output Format:

When creating a numeric data type, the ‘::numeric’ data annotation is required.

Query

SELECT *FROM cypher('graph_name', $$    RETURN 1.0::numeric $$) AS (numeric_result agtype);

Result:

numeric_result

1.0::numeric

(1 row)

Bool

AGE provides the standard Cypher type boolean. The boolean type can have several states: “true”, “false”, and a third state, “unknown”, which is represented by the Agtype null value.

Boolean constants can be represented in Cypher queries by the keywords TRUE, FALSE, and NULL.

Note that the parser automatically understands that TRUE and FALSE are of type boolean, but this is not so for NULL because that can have any type. So in some contexts you might have to cast NULL to boolean explicitly, for example NULL::boolean. Conversely, the cast can be omitted from a string-literal Boolean value in contexts where the parser can deduce that the literal must be of type boolean.

Input/Output Format

Query

SELECT *FROM cypher('graph_name', $$    RETURN TRUE $$) AS (boolean_result agtype);

Unlike Postgres, AGE’s boolean outputs as the full word, ie. true and false as opposed to t and f.

Result:

boolean_result

true

(1 row)

String

Agtype strings String literals can contain the following escape sequences:

Escape Sequence

Character

\t

Tab

\b

Backspace

\n

Newline

\r

Carriage Return

\f

Form Feed

\’

Single Quote

\”

Double Quote

\\

Backslash

\uXXXX

Unicode UTF-16 code point (4 hex digits must follow the \u)

Input/Output Format

Use single (‘) quotes to identify a string. The output will use double (“) quotes.

Query

SELECT *FROM cypher('graph_name', $$    RETURN ‘This is a string’ $$) AS (string_result agtype);

Result:

string_result

“This is a string”

(1 row)

Composite Data Types

List

All examples will use the WITH clause and RETURN clause.

Lists in general

A literal list is created by using brackets and separating the elements in the list with commas.

Query

SELECT *FROM cypher('graph_name', $$    WITH [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] as lst    RETURN lst $$) AS (lst agtype);

Result:

lst

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

(1 row)

NULL in a List

A list can hold the value null, unlike when a null is an independent value, it will appear as the word ‘null’ in a list

Query

SELECT *FROM cypher('graph_name', $$    WITH [null] as lst    RETURN lst $$) AS (lst agtype);

Result:

lst

[null]

(1 row)

Access Individual Elements

To access individual elements in the list, we use the square brackets again. This will extract from the start index and up to but not including the end index.

Query

SELECT *FROM cypher('graph_name', $$    WITH [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] as lst    RETURN lst[3] $$) AS (element agtype);

Result:

element

3

(1 row)

Map Elements in Lists

Query

SELECT *FROM cypher('graph_name', $$   WITH [0, {key: 'key_value'}, 2, 3, 4, 5, 6, 7, 8, 9, 10] as lst    RETURN lst $$) AS (map_value agtype);

Result:

map_value

[0, {"key": "key_value"}, 2, 3, 4, 5, 6, 7, 8, 9, 10]

(1 row)

Accessing Map Elements in Lists

Query

SELECT *FROM cypher('graph_name', $$   WITH [0, {key: 'key_value'}, 2, 3, 4, 5, 6, 7, 8, 9, 10] as lst    RETURN lst[1].key $$) AS (map_value agtype);

Result:

map_value

“key_value”

(1 row)

Negative Index Access

You can also use negative numbers, to start from the end of the list instead.

Query

SELECT *FROM cypher('graph_name', $$    WITH [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] as lst    RETURN lst[-3] $$) AS (element agtype);

Result:

element

8

(1 row)

Index Ranges

Finally, you can use ranges inside the brackets to return ranges of the list.

Query

SELECT *FROM cypher('graph_name', $$    WITH [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] as lst    RETURN lst[0..3] $$) AS (element agtype);

Result:

element

[0, 1, 2]

(1 row)

Negative Index Ranges

Query

SELECT *FROM cypher('graph_name', $$    WITH [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] as lst    RETURN lst[0..-5] $$) AS (lst agtype);

Result:

lst

[0, 1, 2, 3, 4, 5]

(1 row)

Positive Slices

Query

SELECT *FROM cypher('graph_name', $$    WITH [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] as lst    RETURN lst[..4] $$) AS (lst agtype);

Result:

lst

[0, 1, 2, 3]

(1 row)

Negative Slices

Query

SELECT *FROM cypher('graph_name', $$    WITH [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] as lst    RETURN lst[-5..] $$) AS (lst agtype);

Result:

lst

[6, 7, 8, 9, 10]

(1 row)

Out-of-bound slices are simply truncated, but out-of-bound single elements return null.

Query

SELECT *FROM cypher('graph_name', $$    WITH [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] as lst    RETURN lst[15] $$) AS (element agtype);

Result:

element

[0, 1, 2, 3]

(1 row)

Query

SELECT *FROM cypher('graph_name', $$    WITH [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] as lst    RETURN lst[5..15] $$) AS (element agtype);

Result:

element

[5, 6, 7, 8, 9, 10]

(1 row)

Map

Maps can be constructed using Cypher.

Literal Maps with Simple Data Types

You can construct a simple map with simple agtypes

Query

SELECT *FROM cypher('graph_name', $$    WITH {int_key: 1, float_key: 1.0, numeric_key: 1::numeric, bool_key: true, string_key: 'Value'} as m

    RETURN m $$) AS (m agtype);

Result:

m

{"int_key": 1, "bool_key": true, "float_key": 1.0, "string_key": "Value", "numeric_key": 1::numeric}

(1 row)

Literal Maps with Composite Data Types

A map can also contain Composite Data Types, i.e. lists and other maps.

Query

SELECT *FROM cypher('graph_name', $$    WITH {listKey: [{inner: 'Map1'}, {inner: 'Map2'}], mapKey: {i: 0}} as m    RETURN m $$) AS (m agtype);

Result:

m

{"mapKey": {"i": 0}, "listKey": [{"inner": "Map1"}, {"inner": "Map2"}]}

(1 row)

Property Access of a map

Query

SELECT *FROM cypher('graph_name', $$    WITH {int_key: 1, float_key: 1.0, numeric_key: 1::numeric, bool_key: true, string_key: 'Value'} as m    RETURN m.int_key $$) AS (int_key agtype);

Result:

int_key

1

(1 row)

Accessing List Elements in Maps

Query

SELECT *FROM cypher('graph_name', $$    WITH {listKey: [{inner: 'Map1'}, {inner: 'Map2'}], mapKey: {i: 0}} as m

    RETURN m.listKey[0] $$) AS (m agtype);

Result:

m

{"inner": "Map1"}

(1 row)

Simple Entities

An entity has a unique, comparable identity which defines whether or not two entities are equal.

An entity is assigned a set of properties, each of which are uniquely identified in the set by the irrespective property keys.

GraphId

Simple entities are assigned a unique graphid. A graphid is a unique composition of the entity's label id and a unique sequence assigned to each label. Note that there will be overlap in ids when comparing entities from different graphs.

Labels

A label is an identifier that classifies vertices and edges into certain categories.

See CREATE clause for information about how to make entities with labels.

Properties

Both vertices and edges may have properties. Properties are attribute values, and each attribute name should be defined only as a string type.

Vertex

Data Format:

Attribute Name

Description

Id

graphid for this vertex

label

Name of the label this vertex has

properties

Properties associated with this vertex

{id:1; label: ‘label_name’; properties: {prop1: value1, prop2: value2}}::vertex

Type Casting a Map to a Vertex

Query

SELECT * FROM cypher('graph_name', $$         WITH {id: 0, label: "label_name", properties: {i: 0}}::vertex as v         RETURN v $$) AS (v agtype);

Result:

v

{"id": 0, "label": "label_name", "properties": {"i": 0}}::vertex

(1 row)

Edge

An edge is an entity that encodes a directed connection between exactly two nodes, thesource node and the target node. An outgoing edge is a directed relationship from the point of view of its source node. An incoming edge is a directed relationship from the point of view of its target node. An edge is assigned exactly one edge type.

Data Format

Attribute Name

Description

id

graphid for this edge

startid

graphid for the incoming edge

endid

graphid for the outgoing edge

label

Name of the label this edge has

properties

Properties associated with this edge

Output:

{id: 3; startid: 1; endid: 2; label: ‘edge_label’ properties{prop1: value1, prop2: value2}}::edge

Type Casting a Map to an Edge

Query

SELECT * FROM cypher('graph_name', $$         WITH {id: 2, start_id: 0, end_id: 1, label: "label_name", properties: {i: 0}}::edge as e         RETURN e $$) AS (e agtype);

Result:

v

{"id": 2, "label": "label_name", "end_id": 1, "start_id": 0, "properties": {"i": 0}}::edge

(1 row)

Composite Entities

Path

A path is a series of alternating vertices and edges. A path must start with a vertex, and have at least one edge.

Type Casting a Map to a Path

Query

SELECT * FROM cypher('graph_name', $$         WITH [{id: 0, label: "label_name_1", properties: {i: 0}}::vertex,

            {id: 2, start_id: 0, end_id: 1, label: "edge_label", properties: {i: 0}}::edge,

           {id: 1, label: "label_name_2", properties: {}}::vertex

           ]::path as p         RETURN p $$) AS (p agtype);

The result is formatted to improve readability

Result:

p

[

    {"id": 0, "label": "label_name_1", "properties": {"i": 0}}::vertex,

    {"id": 2, "label": "edge_label", "end_id": 1, "start_id": 0, "properties": {"i": 0}}::edge,

    {"id": 1, "label": "label_name_2", "properties": {}}::vertex

]::path

(1 row)

Comparability, Equality, Orderability and Equivalence

AGE already has good semantics for equality within the primitive types (booleans, strings,integers, and floats) and maps. Furthermore, Cypher has good semantics for comparability and orderability for integers, floats, and strings, within each of the types. However, working with values of different types deviates from Postgres’ defined logic and the openCypher specification:

The underlying conceptual model is complex and sometimes inconsistent. This leads to an unclear relationship between comparison operators, equality, grouping, and ORDER BY:

Concepts

The openCypher specification features four distinct concepts related to equality and ordering:

Comparability

Comparability is used by the inequality operators (>, <, >=, <=), and defines the underlying semantics of how to compare two values.

Equality

Equality is used by the equality operators (=, <>), and the list membership operator (IN). It defines the underlying semantics to determine if two values are the same in these contexts. Equality is also used implicitly by literal maps in node and relationship patterns, since such literal maps are merely a shorthand notation for equality predicates.

Orderability

Orderability is used by the ORDER BY clause, and defines the underlying semantics of how to order values.

Equivalence

Equivalence is used by the DISTINCT modifier and by grouping in projection clauses (WITH,RETURN), and defines the underlying semantics to determine if two values are the same in these contexts.

Comparability and equality

Comparison operators need to function as one would expect comparison operators to function - equality and comparability. But, at the same time, they need to allow the sorting of column data - equivalence and orderability.

Unfortunately, it may not be possible to implement separate comparison operators for equality and comparison operations, and, equivalence and orderability operations, in PostgreSQL, for the same query. So we prioritize equivalence and orderability over equality and comparability to allow for ordering of output data.

Comparability

Comparability is defined between any pair of values, as specified below.

Entities

p1 < p2 <=> [n1, r1, n3] < [n1, r2, n2] <=> n1 < n1 || (n1 = n1 && [r1, n3] < [r2, n2]) <=> false || (true && [r1, n3] < [r2, n2])<=> [r1, n3] < [r2, n2] <=> r1 < r2 || (r1 = r2 && n3 < n2) <=> true || (false && false) <=> true

Orderability Between different Agtypes

The ordering of different Agtype, when using <, <=, >, >= from smallest value to largest value is:

  1. Edge
  2. Path
  3. Map
  4. Vertex
  5. Edge
  6. Array
  7. String
  8. Bool
  9. Numeric, Integer, Float
  10. NULL

Note: This is subject to change in future releases.

Operator Precedence

Operator precedence in AGE is shown below:

Precedence

Operator

1

.

Property Access

2

[]

Map and List Subscripting

()

Function Call

3

STARTS WITH

Case-sensitive prefix searching on strings

ENDS WITH

Case-sensitive suffix searching on strings

CONTAINS

Case-sensitive inclusion searching on strings

4

-

Unary Minus

5

IN

Checking if an element exists in a list

IS NULL

Checking a value is NULL

IS NOT NULL

Checking a value is not NULL

6

^

Exponentiation

7

* / %

Multiplication, division and remainder

8

+ -

Addition and Subtraction

9

= <>

For relational = and ≠ respectively

< <=

For relational < and ≤ respectively

> >=

For relational > and ≥ respectively

10

NOT

Logical NOT

11

AND

Logical AND

12

OR

Logical OR

The AGE Cypher Query Format

Cypher queries are constructed using a function called cypher in ag_catalog which returns a Postgres SETOF records.

Cypher()

Cypher() executes the cypher query passed as an argument.

Syntax cypher(graph_name, query_string, parameters)

Returns

A SETOF records

Arguments:

Argument Name

Description

graph_name

The target graph for the Cypher query.

query_string

The Cypher query to be executed.

parameters

An optional map of parameters used for stored procedure. Default is NULL. See Stored Procedures for details

Considerations:

Query:

SELECT * FROM cypher('graph_name', $$ /* Cypher Query Here */  $$) AS (result1 agtype, result2 agtype);

Cypher in an Expression

Cypher may not be used as part of an expression, use a subquery instead. See Advanced Cypher Queries for information about how to use Cypher queries with Expressions

SELECT Clause

Calling Cypher in the SELECT clause as an independent column is not allowed. However Cypher may be used when it belongs as a conditional.

Not Allowed:

SELECT     cypher('graph_name', $$

         MATCH (v:Person)

         RETURN v.name

     $$);

ERROR:  cypher(...) in expressions is not supported LINE 3:         cypher('graph_name', $$                ^ HINT:  Use subquery instead if possible.

Using CREATE with CTEs

Query:

WITH graph_query as (    SELECT *        FROM cypher('graph_name', $$        MATCH (n), (m)

        WHERE n.name = 'A' AND m.name = 'B'

        CREATE (n)-[r:RELTYPE {name: n.name + '->' + m.name }]->(m)        RETURN n.value, m.value, r.name    $$) as (n_value agtype, m_value agtype, edge_name agtype) )SELECT * FROM graph_queryJOIN schema_name.table_name tON t.name = qraph_query.name;

Type Casting

Type Casting in Cypher

You can typecaste one agtype type to another with the syntax: ::datatype

Query

SELECT * FROM cypher('graph_name', $$    RETURN 1::float $$) AS (float_result VARCHAR(50));

The float value 1.0 will be returned.

float_result

1.0

1 row(s) returned

Type Casting to Postgres Data Types

Some Agtype values can be converted to built in Postgres types, other types are currently not supported.

Agtype

Postgres Data Type

int

Cannot be cast

string

Varchar and Char

bool

boolean

float

float

numeric

Cannot be cast

Vertex

Cannot be cast

Edge

Cannot be cast

Path

Cannot be cast

Type Coercion With the Cypher Function

The cypher function call is capable of coercing agtype to certain postgres types.

Query:

SELECT * FROM cypher('graph_name', $$    RETURN 'this is a string' $$) AS (string_result agtype);

Query:

SELECT * FROM cypher('graph_name', $$    RETURN 'this is a string' $$) AS (string_result VARCHAR(50));

Aggregation

Aggregation does not currently support grouping by non-aggregate columns. Any reference to a non-aggregate value in a RETURN statement that contains an aggregate function will be ambiguous and non-deterministic.

Developers Note Aggregation will be heavily updated in the next release.

See aggregation functions for more details.

Prepared Statements

Cypher can run a read query within a Prepared Statement. When using parameters with stored procedures, An SQL Parameter must be placed in the cypher function call. See The AGE Query Format for details.

Developers NoteCREATE, SET, and REMOVE are not currently compatible with Stored Procedures.

Cypher Parameter Format

A cypher parameter is in the format of a '$' followed by an identifier. Unlike Postgres parameters, Cypher parameters start with a letter, followed by an alphanumeric string of arbitrary length.

Example: $parameter_name

Prepared Statements Preparation

Preparing Prepared Statements in cypher is an extension of Postgres' stored procedure system. Use the PREPARE clause to create a query with the Cypher Function call in it. Do not place Postgres style parameters in the cypher query call, instead place Cypher parameters in the query and place a Postgres parameter as the third argument in the Cypher function call.

PREPARE cypher_stored_procedure(agtype) ASSELECT *FROM cypher('expr', $$    MATCH (v:Person)    WHERE v.name = $name //Cypher parameter    RETURN v $$, $1) //An SQL Parameter must be placed in the cypher function call

AS (v agtype);

Prepared Statements Execution

When executing the prepared statement, place an agtype map with the parameter values where the Postgres Parameter in the Cypher function call is. The value must be an agtype map or an error will be thrown. Exclude the '$' for parameter names.

EXECUTE cypher_prepared_statement('{"name": "Tobias"}');

Clauses

MATCH

The MATCH clause allows you to specify the patterns Cypher will search for in the database. This is the primary way of getting data into the current set of bindings. It is worth reading up more on the specification of the patterns themselves in Patterns.

MATCH is often coupled to a WHERE part which adds restrictions, or predicates, to the MATCH patterns, making them more specific. The predicates are part of the pattern description, and should not be considered a filter applied only after the matching is done. This means that WHERE should always be put together with the MATCH clause it belongs to.

MATCH can occur at the beginning of the query or later, possibly after a WITH. If it is the first clause, nothing will have been bound yet, and Cypher will design a search to find the results matching the clause and any associated predicates specified in any WHERE part. Vertices and edges found by this search are available as bound pattern elements, and can be used for pattern matching of sub-graphs. They can also be used in any future clauses, where Cypher will use the known elements, and from there find further unknown elements.

Cypher is declarative, and so usually the query itself does not specify the algorithm to use to perform the search. Predicates in WHERE parts can be evaluated before pattern matching, during pattern matching, or after finding matches.

Basic vertex finding

Get all Vertices

By just specifying a pattern with a single vertex and no labels, all vertices in the graph will be returned.

Query

SELECT * FROM cypher('graph_name', $$MATCH (v)RETURN v $$) as (v agtype);

Returns all the vertices in the database.

v

{id: 0; label: ‘Person’; properties{name: ‘Charlie Sheen’}}::vertex

{id: 1; label: ‘Person’; properties{name: ‘Martin Sheen’}}::vertex

{id: 2; label: ‘Person’; properties{name: ‘Michael  Douglas’}}::vertex

{id: 3; label: ‘Person’; properties{name: ‘Oliver Stone’}}::vertex

{id: 4; label: ‘Person’; properties{name: ‘Rob Reiner’}}::vertex

{id: 5; label: ‘Movie’; properties{name: ‘Wall Street’}}::vertex

{id: 6; label: ‘Movie’; properties{title: ‘The American President’}}::vertex

7 row(s) returned

Get all vertices with a label

Getting all vertices with a label on them is done with a single node pattern where the vertex has a label on it.

Query

SELECT * FROM cypher('graph_name', $$MATCH (movie:Movie)RETURN movie.title $$) as (title agtype);

Returns all the movies in the database.

title

‘Wall Street’

‘The American President’

2 row(s) returned

Related Vertices

The symbol -[]- means related to, without regard to type or direction of the edge.

Query

SELECT * FROM cypher('graph_name', $$MATCH (director {name: 'Oliver Stone'})-[]-(movie)RETURN movie.title $$) as (title agtype);

Returns all the movies directed by 'Oliver Stone'

title

‘Wall Street’

1 row(s) returned

Match with labels

To constrain your pattern with labels on vertices, you add it to your vertex in the pattern, using the label syntax.

Query

SELECT * FROM cypher('graph_name', $$MATCH (:Person {name: 'Oliver Stone'})-[]-(movie:Movie)RETURN movie.title $$) as (title agtype);

Returns any vertices connected with the Person 'Oliver' that are labeled Movie.

title

‘Wall Street’

1 row(s) returned

Edge basics

Outgoing Edges

When the direction of an edge is of interest, it is shown by using -> or <-.

Query

SELECT * FROM cypher('graph_name', $$MATCH (:Person {name: 'Oliver Stone'})-[]->(movie)RETURN movie.title $$) as (title agtype);

Returns any vertices connected with the Person'Oliver' by an outgoing edge.

title

‘Wall Street’

1 row(s) returned

Directed Edges and variable

If a variable is required, either for filtering on properties of the edge, or to return the edge, this is how you introduce the variable.

Query

SELECT * FROM cypher('graph_name', $$MATCH (:Person {name: 'Oliver Stone'})-[r]->(movie)RETURN type(r) $$) as (type agtype);

Returns the type of each outgoing edge from 'Oliver'.

title

‘DIRECTED’

1 row(s) returned

Match on edge type

When you know the edge type you want to match on, you can specify it by using a colon together with the edge type.

Query

SELECT * FROM cypher('graph_name', $$MATCH (:Movie {title: 'Wall Street'})<-[:ACTED_IN]-(actor)RETURN actor.name $$) as (actors_name agtype);

Returns all actors that ACTED_IN'Wall Street'.

actors_name

‘Charlie Sheen’

‘Martin Sheen’

‘Michael  Douglas’

3 row(s) returned

Match on edge type and use a variable

If you both want to introduce a variable to hold the edge, and specify the edge type you want, just add them both.

Query

SELECT * FROM cypher('graph_name', $$MATCH ({title: 'Wall Street'})<-[r:ACTED_IN]-(actor)RETURN r.role $$) as (role agtype);

Returns ACTED_IN roles for 'Wall Street'.

role

‘Gordon Gekko’

‘Carl Fox’

‘Bud Fox’

3 row(s) returned

Multiple Edges

Edges can be expressed by using multiple statements in the form of ()-[]-(), or they can be strung together.

Query

SELECT * FROM cypher('graph_name', $$    MATCH (charlie {name: 'Charlie Sheen'})-[:ACTED_IN]->(movie)<-[:DIRECTED]-(director)    RETURN movie.title, director.name $$) as (title agtype, name agtype);

Returns the movie 'Charlie Sheen' acted in and its director.

title

name

‘Wall Street’

‘Oliver Stone’

1 row(s) returned

WITH

Introduction

Using WITH, you can manipulate the output before it is passed on to the following query parts. The manipulations can be of the shape and/or number of entries in the result set.

WITH can also, like RETURN, alias expressions that are introduced into the results using the aliases as the binding name.

WITH is also used to separate the reading of the graph from updating of the graph. Every part of a query must be either read-only or write-only. When going from a writing part to a reading part, the switch can be done with an optional WITH clause.

Filter on results

Results passed through a WITH clause can be filtered on.

Query

SELECT *FROM cypher('graph_name', $$MATCH (david {name: 'David'})-[:FRIEND]-(otherPerson)WITH otherPerson.name as name, otherPerson.age as age, otherPerson.freetonight as free_tonightWHERE age > 21 and free_tonight = TRUERETURN name $$) as (name agtype);

The name of the person connected to 'David' with the at least more than one outgoing relationship will be returned by the query.

Result

name

"Anders"

1 row

RETURN  

In the RETURN part of your query, you define which parts of the pattern you are interested in. It can be nodes, relationships, or properties on these.

Return nodes

To return a node, list it in the RETURN statement.

Query

SELECT *

FROM cypher('graph_name', $$    MATCH (n {name: 'B'})    RETURN n $$) as (n agtype);

The example will return the node.

Result

n

{id: 0; label: ‘’ properties: {name: ‘B’}}::vertex

(1 row)

Return edges

To return n edge, just include it in the RETURN list.

Query

SELECT *FROM cypher('graph_name', $$    MATCH (n)-[r:KNOWS]->()    WHERE n.name = 'A'    RETURN r $$) as (r agtype);

The relationship is returned by the example.

r

{id: 2; startid: 0; endid: 1; label: ‘KNOWS’ properties: {}}::edge

(1 row)

Return property

To return a property, use the dot separator, like this:

Query

SELECT *FROM cypher('graph_name', $$    MATCH (n {name: 'A'})    RETURN n.name $$) as (name agtype);

The value of the property name gets returned.

Result

name

‘A’

(1 row)

Variable with uncommon characters

To introduce a placeholder that is made up of characters that are not contained in the English alphabet, you can use the ` to enclose the variable, like this:

Query

SELECT *FROM cypher('graph_name', $$    MATCH (`This isn\'t a common variable`)    WHERE `This isn\'t a common variable`.name = 'A'    RETURN `This isn\'t a common variable`.happy $$) as (happy agtype);

The node with name "A" is returned.

Result

happy

"Yes!"

(1 row)

Aliasing a field

If the name of the field should be different from the expression used, you can rename it by changing the name in the column list definition.

Query

SELECT *FROM cypher('graph_name', $$    MATCH (n {name: 'A'})    RETURN n.name $$) as (objects_name agtype);

Returns the age property of a node, but renames the field.

Result

objects_name

‘A’

(1 row)

Optional properties

If a property might or might not be there, you can still select it as usual. It will be treated as null if it is missing.

Query

SELECT *FROM cypher('graph_name', $$    MATCH (n)    RETURN n.age $$) as (age agtype);

This example returns the age when the node has that property, or null if the property is not there.

Result

age

55

NULL

(2 rows)

Other expressions

Any expression can be used as a return item—literals, predicates, properties, functions, and everything else.

Query

SELECT *FROM cypher('graph_name', $$    MATCH (a)    RETURN a.age > 30, ‘I'm a literal’, id(a) $$) as (older_than_30 agtype, literal agtype, id agtype);

Returns a predicate, a literal and function call with a pattern expression parameter.

Result

older_than_30

literal

id

true

‘I’m a literal’

1

(1 row)

Unique results

DISTINCT retrieves only unique records depending on the fields that have been selected to output.

Query

SELECT *FROM cypher('graph_name', $$MATCH (a {name: 'A'})-[]->(b)RETURN DISTINCT b $$) as (b agtype);

The node named "B" is returned by the query, but only once.

Result

b

{id: 1; label: ‘’ properties: {name: ‘B’}}::vertex

(1 row)

ORDER BY

ORDER BY is a sub-clause following WITH, and it specifies that the output should be sorted and how.

Developers Note 

In future releases, ORDER BY will be compatible with the RETURN clause, but is currently not available.

Introduction

Note that you cannot sort on nodes or relationships, just on properties on these. ORDER BY relies on comparisons to sort the output, see Ordering and comparison of values. In terms of scope of variables, ORDER BY follows special rules, depending on if the projecting RETURN or WITH clause is either aggregating or DISTINCT. If it is an aggregating or DISTINCT projection, only the variables available in the projection are available. If the projection does not alter the output cardinality (which aggregation and DISTINCT do), variables available from before the projecting clause are also available. When the projection clause shadows already existing variables, only the new variables are available.Lastly, it is not allowed to use aggregating expressions in the ORDER BY sub-clause if they are not also listed in the projecting clause. This last rule is to make sure that ORDER BY does not change the results, only the order of them

Order nodes by property

ORDER BY is used to sort the output.

Query

SELECT *FROM cypher('graph_name', $$    MATCH (n)    WITH n.name as name, n.age as age    ORDER BY n.name    RETURN name, age $$) as (name agtype, age agtype);

The nodes are returned, sorted by their name.

Result

name

age

"A"

34

"B"

34

"C"

32

(1 row)

Order nodes by multiple properties

You can order by multiple properties by stating each variable in the ORDER BY clause. Cypher will sort the result by the first variable listed, and for equal values, go to the next property in the ORDER BY clause, and so on.

Query

SELECT *FROM cypher('graph_name', $$    MATCH (n)    WITH n.name as name, n.age as age    ORDER BY n.age, n.name    RETURN name, age $$) as (name agtype, age agtype);

This returns the nodes, sorted first by their age, and then by their name.

Result

name

age

"C"

32

"A"

34

"B"

34

(1 row)

Order nodes in descending order

By adding DESC[ENDING] after the variable to sort on, the sort will be done in reverse order.

Query

SELECT *FROM cypher('graph_name', $$    MATCH (n)    WITH n.name AS name, n.age AS age    ORDER BY n.name DESC    RETURN name, age $$) as (name agtype, age agtype);

The example returns the nodes, sorted by their name in reverse order.

Result

name

age

"C"

32

"B"

34

"A"

34

(3 rows)

Ordering null

When sorting the result set, null will always come at the end of the result set for ascending sorting,and first when doing descending sort.

Query

SELECT *FROM cypher('graph_name', $$    MATCH (n)    WITH n.name AS name, n.age AS age, n.height    ORDER BY n.height    RETURN name, age, height $$) as (name agtype, age agtype, height agtype);

The nodes are returned sorted by the length property, with a node without that property last.

Result

name

age

"A"

34

170

"C"

32

185

"B"

34

<NULL>

(3 rows)

WHERE

WHERE is a subclause of MATCH that puts restrictions on the MATCH clause.

In the case of multiple MATCH clauses, the predicate in WHERE is always a part of the patterns in the directly preceding MATCH. Both results and performance may be impacted if the WHERE is associated with the wrong MATCH clause.

Filter on a Vertex Property

To filter on a vertex property, write your clause after the WHERE keyword.

Query

SELECT * FROM cypher('graph_name', $$    MATCH (n)    WHERE n.age < 30     RETURN n.name, n.age $$) as (name agtype, age agtype);

The name and age values for the 'Tobias' node are returned because he is less than 30 years of age.

name

age

‘Tobias’

26

1 row(s) returned

Filter on an Edge Property

To filter on an edge property, write your clause after the WHERE keyword.

Query:

SELECT * FROM cypher('graph_name', $$    MATCH (n)-[k:KNOWS]->(f)    WHERE k.since < 2000     RETURN f.name, f.age, f.email $$) as (name agtype, age agtype, email agtype);

The name, age and email values for the 'Peter' node are returned because Andrés has known him since before 2000.

name

age

email

‘Peter’

35

‘peter_n@example.com’

1 row(s) returned

Using Multiple Filters

You can use the boolean operators AND, OR and NOT.

Query:

SELECT * FROM cypher('graph_name', $$    MATCH (n)    WHERE (n.age < 30 AND n.name = 'Tobias')

        OR NOT (n.name ='Tobias' OR n.name = 'Peter')    RETURN n.name, n.age $$) as (name agtype, age agtype);

Results:

name

age

‘Andres’

36

‘Tobias’

25

‘Peter’

35

3 row(s) returned

Filter On Properties With STARTS WITH

Perform case-sensitive prefix searching on strings

Query:

SELECT * FROM cypher('graph_name', $$    MATCH (n)    WHERE n.name STARTS WITH 'P'    RETURN n.name, n.age $$) as (name agtype, age agtype);

Results:

name

age

‘Peter’

35

3 row(s) returned

Filter On Properties With ENDS WITH

Perform case-sensitive suffix searching on strings

Query:

SELECT * FROM cypher('graph_name', $$    MATCH (n)    WHERE n.name ENDS WITH 's'    RETURN n.name, n.age $$) as (name agtype, age agtype);

Results:

name

age

‘Andres’

36

‘Tobias’

25

3 row(s) returned

Filter On Properties With CONTAINS

Perform case-sensitive inclusion searching in strings

Query:

SELECT * FROM cypher('graph_name', $$    MATCH (n)    WHERE n.name CONTAINS 'e'    RETURN n.name, n.age $$) as (name agtype, age agtype);

Results:

name

age

‘Andres’

36

‘Peter’

35

3 row(s) returned

Filter On Properties With Exists

Use the exists() function to only include nodes or relationships in which a property exists.

Query:

SELECT * FROM cypher('graph_name', $$MATCH (n)WHERE exists(n.belt)RETURN n.name, n.belt $$) as (name agtype, belt agtype);

The name and belt for the 'Andres' node are returned because he is the only one with a belt property

name

belt

‘Andres’

‘white’

1 row(s) returned

Filter On Patterns With Exists

Patterns are not only expressions, they are also predicates when used with the EXISTS subclause. The only limitation to your pattern is that you must be able to express it in a single path. You cannot use commas between multiple paths like you do in MATCH. You can achieve the same effect by combining multiple patterns with AND. Note that you cannot introduce new variables here.

Although it might look very similar to the MATCH patterns, the WHERE clause is all about eliminating matched subgraphs. MATCH (a)-[]->(b) is very different from WHERE EXISTS((a)-[]->(b)). The first will produce a subgraph for every path it can find between a and b, whereas the latter will eliminate any matched subgraphs where a and b do not have a directed relationship chain between them.

Query:

SELECT * FROM cypher('graph_name', $$    MATCH (tobias {name: 'Tobias'}), (others)    WHERE EXISTS((tobias)<-[]-(others))    RETURN others.name, others.age $$) as (name agtype, age agtype);

name

age

‘Andres’

36

1 row(s) returned

Filter on patterns using NOT

The NOT operator can be used to exclude a pattern.

Query:

SELECT * FROM cypher('graph_name', $$    MATCH (persons), (peter {name: 'Peter'})    WHERE NOT EXISTS((persons)-[]->(peter))    RETURN persons.name, persons.age $$) as (name agtype, age agtype);

Name and age values for nodes that do not have an outgoing relationship to the 'Peter' node are returned.

name

age

‘Tobias’

25

‘Peter’

35

2 row(s) returned

Filter on patterns with properties

You can also add properties to your patterns.

Query

SELECT * FROM cypher('graph_name', $$MATCH (n)WHERE EXISTS((n)-[:KNOWS]->({name: 'Tobias'}))RETURN n.name, n.age $$) as (name agtype, age agtype);

Finds all name and age values for nodes that have a KNOWS relationship to a node with the name 'Tobias.'

name

age

‘Andres’

36

1 row(s) returned

IN Operator with Lists

To check if an element exists in a list, you can use the IN operator.

Query

SELECT * FROM cypher('graph_name', $$    MATCH (a)    WHERE a.name IN ['Peter', 'Tobias']    RETURN a.name, a.age $$) as (name agtype, age agtype)

This query shows how to check if a property exists in a literal list

name

age

‘Tobias’

25

‘Peter’

35

2 row(s) returned

CREATE

The CREATE clause is used to create graph vertices and edges.

Terminal CREATE clauses

A create clause that is not followed by another clause is called a terminal clause. When a cypher query ends with a terminal clause, no results will be returned from the cypher function call. However, the cypher function call still requires a column list definition. When cypher ends with a terminal node, define a single value in the column list definition: no data will be returned in this variable.

Query

SELECT * FROM cypher('graph_name', $$    CREATE /* Create clause here, no following clause */ $$) as (a agtype);

a

0 row(s) returned

Create Vertices

Create single vertex

Creating a single vertex is done by issuing the following query.

Query

SELECT * FROM cypher('graph_name', $$    CREATE (n) $$) as (v agtype);

Nothing is returned from this query.

v

(0 rows)

Create multiple vertices

Creating multiple vertices is done by separating them with a comma.

Query

SELECT * FROM cypher('graph_name', $$    CREATE (n), (m) $$) as (v agtype);

Result

a

0 row(s) returned

Create a vertex with a label

To add a label when creating a vertex, use the syntax below.

Query

SELECT * FROM cypher('graph_name', $$    CREATE (:Person) $$) as (v agtype);

Nothing is returned from this query.

Result

v

0 row(s) returned

Create vertex and add labels and properties

When creating a new vertex with labels, you can add properties at the same time.

Query

SELECT * FROM cypher('graph_name', $$    CREATE (:Person {name: ‘Andres’, title: ‘Developer’) $$) as (n agtype);

Nothing is returned from this query.

Result

n

(0 rows)

Return created node

Creating a single node is done by issuing the following query.

Query

SELECT * FROM cypher('graph_name', $$    CREATE (a {name: ‘Andres’)

    RETURN a $$) as (a agtype);

The newly-created node is returned.

Result

a

{id: 0; label: ‘’; properties: {name: ‘Andres’}}::vertex

(1 row)

Create Edges

Create an edge between two nodes

To create an edge between two vertices, we first get the two vertices. Once the nodes are loaded, we simply create an edge between them.

Query

SELECT * FROM cypher('graph_name', $$    MATCH (a:Person), (b:Person)    WHERE a.name = 'Node A' AND b.name = 'Node B'    CREATE (a)-[e:RELTYPE]->(b)    RETURN e $$) as (e agtype);

The created edge is returned by the query.

Result

e

{id: 3; startid: 0, endid: 1; label: ‘RELTYPE’; properties: {}}::edge

(1 row)

Create an edge and set properties

Setting properties on edges is done in a similar manner to how it’s done when creating vertices. Note that the values can be any expression.

Query

SELECT * FROM cypher('graph_name', $$    MATCH (a:Person), (b:Person)    WHERE a.name = 'Node A' AND b.name = 'Node B'    CREATE (a)-[e:RELTYPE {name:a.name + ‘<->’ + b.name}]->(b)    RETURN e $$) as (e agtype);

The newly-created edge is returned by the example query.

Result

e

{id: 3; startid: 0, endid: 1; label: ‘RELTYPE’; properties: {name: ‘Node A<->Node B’}}::edge

(1 row)

Create a full path

When you use CREATE and a pattern, all parts of the pattern that are not already in scope at this time will be created.

Query

SELECT * FROM cypher('graph_name', $$    CREATE p = (andres {name:'Andres'})-[:WORKS_AT]->(neo)<-[:WORKS_AT]-(michael {name:'Michael'})    RETURN p $$) as (p agtype);

This query creates three nodes and two relationships in one go, assigns it to a path variable, and returns it.

Result

p

[

    {id:0; label: ‘’; properties:{name:’Andres’}}::vertex,

    {id: 3; startid: 0, endid: 1; label: ‘WORKS_AT’; properties: {}}::edge,

    {id:1; label: ‘’; properties: {}}::vertex

    {id: 3; startid: 2, endid: 1; label: ‘WORKS_AT’; properties: {}}::edge,

    {id:2; label: ‘’; properties: {name:’Michael’}}::vertex

]::path

(1 row)

SET

The SET clause is used to update labels on nodes and properties on vertices and edges

Terminal SET clauses

A set clause that is not followed by another clause is called a terminal clause. When a cypher query ends with a terminal clause, no results will be returned from the cypher function call. However, the cypher function call still requires a column list definition. When cypher ends with a terminal node, define a single value in the column list definition: no data will be returned in this variable.

Set a property

To set a property on a node or relationship, use SET.

Query

SELECT * FROM cypher('graph_name', $$   MATCH (v {name: 'Andres'})   SET v.surname = 'Taylor' $$) as (v agtype);

The newly changed node is returned by the query.

Result

v

(0 rows)

Return created vertex

Creating a single vertex is done by issuing the following query.

Query

SELECT * FROM cypher('graph_name', $$    MATCH (v {name: 'Andres'})    SET v.surname = 'Taylor'    RETURN v $$) as (v agtype);

The newly changed vertex is returned by the query.

Result

v

{id: 3; label: ‘Person’; properties: {surname:"Taylor", name:"Andres", age:36, hungry:true}}::vertex

(1 row)

Remove a property

Normally you remove a property by using REMOVE, but it’s sometimes handy to do it using the SET command. One example is if the property comes from a parameter.

Query

SELECT * FROM cypher('graph_name', $$    MATCH (v {name: 'Andres'})    SET v.name = NULL    RETURN v $$) as (v agtype);

The node is returned by the query, and the name property is now missing.

Result

v

{id: 3; label: ‘Person’; properties: {surname:"Taylor", age:36, hungry:true}}::vertex

(1 row)

REMOVE

The REMOVE clause is used to remove properties from vertex and edges.

Terminal REMOVE clauses

A remove clause that is not followed by another clause is called a terminal clause. When a cypher query ends with a terminal clause, no results will be returned from the cypher function call. However, the cypher function call still requires a column list definition. When cypher ends with a terminal node, define a single value in the column list definition: no data will be returned in this variable.

Remove a property

Cypher doesn’t allow storing null in properties. Instead, if no value exists, the property is just not there. So, to remove a property value on a node or a relationship, is also done with REMOVE.113

Query

SELECT * FROM cypher('graph_name', $$    MATCH (andres {name: 'Andres'})    REMOVE andres.age    RETURN andres $$) as (andres agtype);

The node is returned, and no property age exists on it.

Result

andres

{id: 3; label: ‘Person’; properties: {name:"Andres"}}::vertex

1 row(s) returned