F.3. apache_age

apache_age — это расширение, добавляющее в Postgres Pro функциональность для работы с графовыми базами данных. AGE — акроним выражения A Graph Extension. Цель проекта — создать единое хранилище, способное работать с данными как реляционной, так и графовой модели, чтобы пользователи могли использовать стандартный ANSI SQL вместе с языком запросов к графам openCypher.

Важно

Пользовательские таблицы в базах данных apache_age содержат столбцы, имеющие системные типы данных reg*, которые ссылаются на OID. В связи с этим обновление основной версии с использованием pg_upgrade не поддерживается.

F.3.1. Установка и подготовка

Расширение apache_age включено в состав Postgres Pro Enterprise. После установки Postgres Pro Enterprise создайте расширение apache_age:

CREATE EXTENSION age;

Загрузите библиотеку apache_age для каждого создаваемого подключения apache_age.

LOAD 'age';

Обычные пользователи должны указывать полный путь для загрузки библиотеки apache_age.

LOAD '$libdir/plugins/age.so';

Добавьте ag_catalog в search_path для упрощения запросов:

SET search_path = ag_catalog, '$user', public;

Чтобы использовать apache_age, обычным пользователям необходимы права USAGE на схему ag_catalog (ниже представлен пример для пользователя db_user):

GRANT USAGE ON SCHEMA ag_catalog TO db_user;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA ag_catalog TO db_user;

F.3.2. Графы

Граф состоит из совокупности вершин и рёбер, где каждый отдельный узел и ребро обладают ассоциативным массивом свойств. Вершина — это базовый объект графа, который может существовать независимо от всего остального в графе. Ребро создаёт направленное соединение между двумя вершинами.

F.3.2.1. Создание графа

Для создания графа используйте функцию create_graph из пространства имён ag_catalog.

create_graph(graph_name text) returns void

Эта функция не возвращает результатов. Если не выводится сообщение об ошибке, граф считается созданным. Необходимые для графа таблицы создаются автоматически.

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

F.3.2.2. Предоставление прав

Суперпользователь может предоставлять права на отдельные существующие графы обычным пользователям (в следующем примере предоставляются права на граф graph1 пользователю db_user):

GRANT USAGE ON SCHEMA graph1 TO db_user;
GRANT ALL PRIVILEGES ON SCHEMA graph1 TO db_user;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA graph1 TO db_user;
GRANT ALL PRIVILEGES ON TABLE graph1._ag_label_vertex TO db_user;

F.3.2.3. Удаление графа

Для удаления графа используйте функцию drop_graph из пространства имён ag_catalog.

drop_graph(graph_name text, cascade boolean) returns void

Эта функция не возвращает результатов. Если не выводится сообщение об ошибке, граф считается удалённым. Рекомендуется установить значение true для параметра cascade, иначе все объекты в графе придётся удалять вручную с помощью DDL-команд SQL.

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

F.3.2.4. Хранение графов в Postgres Pro

При создании графов с помощью apache_age для каждого отдельного графа создаётся пространство имён Postgres Pro. Имена и пространства имён созданных графов можно увидеть в таблице ag_graph пространства имён ag_catalog:

SELECT create_graph('new_graph');

NOTICE:  graph 'new_graph' has been created
 create_graph
--------------

(1 row)

SELECT * FROM ag_catalog.ag_graph;

   name    | namespace
-----------+-----------
 new_graph | new_graph
(1 row)

После создания графа в его пространстве имён будут созданы две таблицы для хранения вершин и рёбер: _ag_label_vertex и _ag_label_edge. Они будут родительскими таблицами для любой новой метки вершины или ребра. В запросе ниже показано, как получить метки рёбер и вершин для всех графов в базе данных.

-- До создания новой метки вершины.
SELECT * FROM ag_catalog.ag_label;

       name       | graph | id | kind |          relation          |        seq_name
------------------+-------+----+------+----------------------------+-------------------------
 _ag_label_vertex | 68484 |  1 | v    | new_graph._ag_label_vertex | _ag_label_vertex_id_seq
 _ag_label_edge   | 68484 |  2 | e    | new_graph._ag_label_edge   | _ag_label_edge_id_seq
(2 rows)

-- Создание новой метки вершины.
SELECT create_vlabel('new_graph', 'Person');
NOTICE:  VLabel 'Person' has been created
 create_vlabel
---------------

(1 row)

-- После создания новой метки вершины.
SELECT * FROM ag_catalog.ag_label;
       name       | graph | id | kind |          relation          |        seq_name
------------------+-------+----+------+----------------------------+-------------------------
 _ag_label_vertex | 68484 |  1 | v    | new_graph._ag_label_vertex | _ag_label_vertex_id_seq
 _ag_label_edge   | 68484 |  2 | e    | new_graph._ag_label_edge   | _ag_label_edge_id_seq
 Person           | 68484 |  3 | v    | new_graph.'Person'         | Person_id_seq
(3 rows)

При создании метки вершины функцией create_vlabel() в пространстве имён new_graph создаётся новая таблица: new_graph.'метка'. То же самое происходит при создании меток рёбер функцией create_elabel(). При создании вершин и рёбер с помощью языка Cypher эти таблицы создаются автоматически.

-- Создание двух вершин и одного ребра.
SELECT * FROM cypher('new_graph', $$
CREATE (:Person {name: 'Daedalus'})-[:FATHER_OF]->(:Person {name: 'Icarus'})
$$) AS (a agtype);
 a
---
(0 rows)

-- Вывод новых созданных таблиц.
SELECT * FROM ag_catalog.ag_label;
       name       | graph | id | kind |          relation          |        seq_name
------------------+-------+----+------+----------------------------+-------------------------
 _ag_label_vertex | 68484 |  1 | v    | new_graph._ag_label_vertex | _ag_label_vertex_id_seq
 _ag_label_edge   | 68484 |  2 | e    | new_graph._ag_label_edge   | _ag_label_edge_id_seq
 Person           | 68484 |  3 | v    | new_graph.'Person'         | Person_id_seq
 FATHER_OF        | 68484 |  4 | e    | new_graph.'FATHER_OF'      | FATHER_OF_id_seq
(4 rows)

Примечание

Не рекомендуется выполнять команды DML или DDL в пространстве имён, зарезервированном для графа.

F.3.3. Формат Cypher-запроса в apache_age

Cypher-запросы создаются с использованием функции cypher в ag_catalog, которая возвращает в Postgres Pro SETOF record.

cypher(graph_name name, query_string cstring, parameters agtype) returns setof record

Выполняет Cypher-запрос, переданный в виде аргумента. Если Cypher-запрос не возвращает результатов, всё равно необходимо задать определение записи. Ассоциативный массив параметров, заданный как parameters, может использоваться только с подготовленными операторами. В противном случае выводится ошибка.

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

F.3.3.1. Язык Cypher в выражении

Язык Cypher нельзя использовать как часть выражения, вместо этого следует использовать подзапрос. Для получения дополнительной информации о том, как использовать Cypher-запросы с выражениями, обратитесь к разделу Расширенные Cypher-запросы.

F.3.3.2. Предложение SELECT

Вызывать язык Cypher в качестве независимого столбца в предложении SELECT не допускается. Однако Cypher может использоваться в качестве условного выражения.

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.

F.3.4. Типы данных — описание agtype

apache_age использует собственный тип данных под названием agtype и возвращает только его. Тип agtype — это надмножество json и вариант реализации jsonb.

F.3.4.1. Простые типы данных

F.3.4.1.1. Тип null

В языке Cypher null используется для обозначения отсутствующих или неопределённых значений. Концептуально null означает «отсутствующее неизвестное значение» и обрабатывается несколько иначе, чем другие значения. Например, при попытке получить свойство из вершины, не имеющей указанного свойства, возвращается null. Большинство выражений, которые принимают null в качестве входных данных, вернут null. Это касается и логических выражений, которые используются в качестве предикатов в предложении WHERE. В этом случае всё, что не является true, интерпретируется как false. null не равен null. Неизвестные значения не считаются равными. Таким образом, выражение null = null выдаёт null и не является true.

Следующий запрос возвращает null как пустой результат.

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

 null_result
--------------

(1 row)

Концепция NULL в agtype и Postgres Pro совпадает с Cypher.

F.3.4.1.2. Тип integer

Тип integer хранит целые числа, то есть числа без дробных составляющих. Целочисленный тип данных — это 64-битное поле, в котором хранятся значения от -9 223 372 036 854 775 808 до 9 223 372 036 854 775 807. Попытки сохранить значения за пределами этого диапазона приведут к ошибке.

Чаще всего используется тип integer, как наиболее сбалансированный с точки зрения ширины диапазона, размера и быстродействия. Тип smallint обычно применяется, только когда крайне важно уменьшить размер данных на диске. Тип bigint предназначен для тех случаев, когда числа не умещаются в диапазон типа integer.

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

 int_result
--------------
1
(1 row)
F.3.4.1.3. Тип float

Тип данных float используется для приближённых числовых значений с переменной точностью, соответствующих стандарту IEEE 754.

Переменная точность здесь выражается в том, что некоторые значения, которые нельзя преобразовать во внутренний формат, сохраняются приближённо, так что полученное значение может несколько отличаться от записанного. Управление подобными расхождениями и их распространение в процессе вычислений являются предметом изучения целых разделов математики и информатики, и здесь не рассматривается. Однако следует отметить следующее:

  • Если нужна точность данных при хранении и вычислениях (например, для денежных сумм), используйте вместо этого тип numeric.

  • Если с этими типами выполняются сложные и важные вычисления, тщательно изучите реализацию операций в используемой среде и особенно поведение в особых случаях (бесконечность, антипереполнение).

  • Проверка равенства двух чисел с плавающей точкой может не всегда давать ожидаемый результат.

Попытка сохранить слишком большие или слишком маленькие значения приведёт к ошибке. Если точность вводимого числа слишком велика, оно будет округлено. При попытке сохранить число, близкое к 0, но непредставимое как отличное от 0, произойдёт ошибка антипереполнения.

Помимо обычных числовых (numeric) значений, в типах с плавающей точкой есть несколько специальных значений:

  • Infinity

  • -Infinity

  • NaN

Они представляют собой специальные значения IEEE 754 «бесконечность», «отрицательная бесконечность» и «не-число» соответственно. При записи этих значений в качестве констант на языке Cypher нужно заключать их в кавычки и приводить к типу, например:

SET x.float_value = '-Infinity'::float

При вводе эти строки распознаются без учёта регистра.

Примечание

Обратите внимание, что IEEE 754 указывает, что NaN не следует сравнивать с любым другим значением с плавающей точкой (включая NaN). Однако, чтобы обеспечить правильную сортировку чисел с плавающей точкой, apache_age считает истинным 'NaN'::float = 'NaN'::float. За дополнительной информацией обратитесь к разделу Сопоставимость и равенство.

Чтобы использовать число с плавающей точкой, укажите десятичное значение.

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

float_result
--------------
1.0
(1 row)
F.3.4.1.4. Тип numeric

Тип numeric позволяет хранить числа с очень большим количеством цифр. Он особенно рекомендуется для хранения денежных сумм и других величин, где важна точность. Вычисления с типом numeric дают точные результаты, где это возможно, например, при сложении, вычитании и умножении. Однако операции со значениями numeric выполняются гораздо медленнее, чем с целыми числами или типами с плавающей точкой.

Ниже мы используем следующие термины: масштаб значения numeric определяет количество десятичных цифр в дробной части, справа от десятичной точки, а точность — общее количество значимых цифр в числе, т. е. количество цифр по обе стороны десятичной точки. Например, число 23.5141 имеет точность 6 и масштаб 4. Целочисленные значения можно считать числами с масштабом 0.

При указании NUMERIC без точности и масштаба создаётся столбец, в котором можно сохранять числовые значения любого масштаба и точности до предела, обусловленного реализацией. В столбце этого типа входные значения не будут приводиться к какому-либо масштабу, тогда как в столбцах numeric с явно заданным масштабом значения подгоняются под этот масштаб. (Стандарт SQL утверждает, что по умолчанию должен устанавливаться масштаб 0, т. е. значения должны приводиться к целым числам. Однако мы считаем это не очень полезным. Если для вас важна переносимость, всегда указывайте точность и масштаб явно.)

Примечание

Максимально допустимая точность, которую можно указать в объявлении типа, равна 1000; если же использовать NUMERIC без указания точности, действуют ограничения, описанные в Таблице 8.2.

Если масштаб значения, которое нужно сохранить, превышает объявленный масштаб столбца, система округлит его до заданного количества цифр после точки. Если же после этого количество цифр слева в сумме с масштабом превысит объявленную точность, произойдёт ошибка.

Числовые значения физически хранятся без каких-либо дополняющих нулей слева или справа. Таким образом, объявляемые точность и масштаб столбца определяют максимальный, а не фиксированный размер хранения. (В этом смысле тип numeric больше похож на тип varchar(n), чем на char(n).) Действительный размер хранения такого значения складывается из двух байт для каждой группы из четырёх цифр и дополнительных трёх-восьми байт.

Помимо обычных чисел тип numeric позволяет использовать специальное значение NaN, что означает «not-a-number» (не число). Любая операция c NaN выдаёт в результате тоже NaN. Записывая это значение в виде константы в команде SQL, его нужно заключать в апострофы, например так: UPDATE table SET x = 'NaN'. Регистр символов в строке NaN не важен.

Примечание

В большинстве реализаций «не число» (NaN) считается не равным любому другому значению (в том числе и самому NaN). Чтобы можно было сортировать числа с плавающей точкой, apache_age вычисляет 'NaN'::numeric = 'NaN':numeric как true. За дополнительной информацией обратитесь к разделу Сопоставимость и равенство.

При округлении значений тип numeric выдаёт число, большее по модулю, тогда как (на большинстве платформ) типы real и double precision выдают ближайшее чётное число.

При создании типа данных numeric требуется аннотация данных ::numeric.

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

numeric_result
--------------
1.0::numeric
(1 row)
F.3.4.1.5. Логические значения

В apache_age есть стандартный Cypher-тип boolean. Тип boolean может иметь следующие состояния: true, false и третье состояние, unknown, которое представляется значением null типа agtype.

Логические константы могут быть представлены в Cypher-запросах следующими ключевыми словами: TRUE, FALSE и NULL.

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

boolean_result
--------------
true
(1 row)

В отличие от Postgres Pro, в apache_age логические значения выводятся как полное слово, т. е. true и false, а не t и f.

F.3.4.1.6. Тип string

Строковые литералы agtype могут содержать следующие спецпоследовательности:

Таблица F.2. Спецпоследовательности

СпецпоследовательностьСимвол
\tТабуляция
\bЗабой
\nНовая строка
\rВозврат каретки
\fПодача формы
\'Апостроф
\"Двойная кавычка
\\Обратная косая черта
\uXXXXКодовая точка Unicode UTF-16 (4 шестнадцатеричные цифры должны следовать за \u)

Используйте апостроф (') для определения строки. В выводе будут использоваться двойные (") кавычки.

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

string_result
--------------
"This is a string"
(1 row)

F.3.4.2. Составные типы данных

F.3.4.2.1. Список

Во всех примерах будут использоваться предложения WITH и RETURN.

Буквальный список создаётся с помощью скобок и разделения элементов списка запятыми.

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

                lst
------------------------------------
 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
(1 row)

Список может содержать значение null, но в отличие от независимого значения null, оно будет отображаться в списке как слово null.

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

  lst
--------
 [null]
(1 row)

Для доступа к отдельным элементам списка также используются квадратные скобки. Значения будут извлекаться от начального индекса до конечного индекса, не включая последний.

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);

 element
---------
 3
(1 row)

Элементы ассоциативного массива в списках:

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);

                       map_value
-------------------------------------------------------
 [0, {"key": "key_value"}, 2, 3, 4, 5, 6, 7, 8, 9, 10]
(1 row)

Обращение к элементам ассоциативного массива в списках:

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);

  map_value
-------------
 "key_value"
(1 row)

Также можно использовать отрицательные числа, чтобы начать с конца списка.

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);

 element
---------
 8
(1 row)

Наконец, можно использовать диапазоны внутри скобок для возвращения диапазонов из списка.

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);

  element
-----------
 [0, 1, 2]
(1 row)

Отрицательные диапазоны индексов:

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);

        lst
--------------------
 [0, 1, 2, 3, 4, 5]
(1 row)

Положительные срезы:

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);

     lst
--------------
 [0, 1, 2, 3]
(1 row)

Отрицательные срезы:

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);

       lst
------------------
 [6, 7, 8, 9, 10]
(1 row)

Срезы, выходящие за пределы, просто усекаются, но отдельные элементы, выходящие за пределы, возвращают null.

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);

 element
---------

(1 row)
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);

       element
---------------------
 [5, 6, 7, 8, 9, 10]
(1 row)
F.3.4.2.2. Ассоциативный массив

С помощью Cypher можно строить ассоциативные массивы.

Можно создать простой ассоциативный массив с простыми значениями типа agtype.

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);

                                                  m
------------------------------------------------------------------------------------------------------
 {"int_key": 1, "bool_key": true, "float_key": 1.0, "string_key": "Value", "numeric_key": 1::numeric}
(1 row)

Ассоциативный массив также может содержать составные типы данных, то есть списки и другие ассоциативные массивы.

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

                                    m
-------------------------------------------------------------------------
 {"mapKey": {"i": 0}, "listKey": [{"inner": "Map1"}, {"inner": "Map2"}]}
(1 row)

Обращение к свойствам ассоциативного массива:

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);

 int_key
---------
 1
(1 row)

Доступ к элементам списка в ассоциативных массивах:

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

         m
-------------------
 {"inner": "Map1"}
(1 row)

F.3.4.3. Простые сущности

Сущность имеет уникальную, сравниваемую идентичность, которая определяет, равны ли две сущности.

Сущности присваивается набор свойств, каждое из которых уникально идентифицируется в наборе соответствующими ключами свойств.

F.3.4.3.1. Идентификатор графа

Простым сущностям присваивается уникальный идентификатор графа (graphid). graphid — это уникальное сочетание идентификатора метки сущности и уникальной последовательности, присвоенной каждой метке. Обратите внимание, что у сущностей из разных графов могут быть одинаковые идентификаторы.

Метка — это идентификатор, который классифицирует вершины и рёбра по определённым категориям.

  • Рёбра должны иметь метку, а вершины — нет.

  • Имена меток вершин и рёбер не могут совпадать.

Обратитесь к описанию предложения CREATE, чтобы узнать, как создавать объекты с метками.

И вершины, и рёбра могут иметь свойства. Свойства представляют собой значения атрибутов, и каждое имя атрибута должно быть строкового типа.

F.3.4.4. Вершина

  • Вершина — это базовая сущность графа, которая может существовать сама по себе.

  • Вершине может быть присвоена метка.

  • У вершины может быть ноль или более исходящих рёбер.

  • У вершины может быть ноль или более входящих рёбер.

Таблица F.3. Формат данных

Имя атрибутаОписание
idИдентификатор графа для данной вершины
labelИмя метки данной вершины
propertiesСвойства, присвоенные данной вершине

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

Приведение ассоциативного массива к вершине:

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

                                v
------------------------------------------------------------------
 {"id": 0, "label": "label_name", "properties": {"i": 0}}::vertex
(1 row)

F.3.4.5. Ребро

Ребро — это сущность, которая представляет собой направленное соединение между двумя узлами: исходным узлом и целевым узлом. Исходящее ребро — это направленная связь с точки зрения исходного узла. Входящее ребро — это направленная связь с точки зрения целевого узла. Ребру присваивается только один тип ребра.

Таблица F.4. Формат данных

Имя атрибутаОписание
idИдентификатор графа для данного ребра
startidИдентификатор графа исходного узла
endidИдентификатор графа целевого узла
labelИмя метки данной вершины
propertiesСвойства, присвоенные данной вершине

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

Приведение ассоциативного массива к ребру:

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);

                                             e
--------------------------------------------------------------------------------------------
 {"id": 2, "label": "label_name", "end_id": 1, "start_id": 0, "properties": {"i": 0}}::edge
(1 row)

F.3.4.6. Составные сущности

Путь — это серия чередующихся вершин и рёбер. Путь должен начинаться с вершины и иметь хотя бы одно ребро.

Приведение списка к пути:

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);

                                                                                                                  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)

F.3.5. Сопоставимость, равенство, упорядочиваемость и эквивалентность

В apache_age уже предусмотрена семантика равенства примитивных типов (логических значений, строк, целых чисел и чисел с плавающей точкой) и ассоциативных массивов. Более того, в Cypher есть возможность сопоставлять и упорядочивать целые числа, числа с плавающей точкой и строки внутри каждого из типов. Однако логика работа со значениями разных типов отличается от логики, используемой в Postgres Pro и спецификации openCypher:

  • Определяется сопоставимость между значениями разных типов. Отклонение между значениями особенно заметно, когда появляется в рамках вычисления предикатов (в WHERE).

  • Запрос с предложением ORDER BY будет успешно выполнен, если переданные ему значения имеют разные типы.

Базовая концептуальная модель сложна и несколько противоречива. В результате взаимосвязь между операторами сравнения, равенством, группировкой и предложением ORDER BY неясна. Согласованность сопоставимости и упорядочиваемости друг с другом, обеспечивается тем, что все типы можно упорядочивать и сопоставлять. Разница между равенством и эквивалентностью, выявляемая указаниями IN, =, DISTINCT и группировкой, в apache_age ограничена проверкой двух экземпляров значения null по отношению друг к другу. В рамках равенства выражение null = null равно null. В эквивалентности, используемой DISTINCT и при группировке значений, два значения null всегда рассматриваются как одно и то же значение. Однако равенство обрабатывает значения null по-разному, если они являются элементами списка или значениями ассоциативного массива.

F.3.5.1. Основные понятия

Спецификация openCypher содержит четыре различные концепции, связанные с равенством и упорядочиванием:

  • Сопоставимость используется операторами неравенства (>, <, >=, <=) и определяет базовую семантику того, как сравнивать два значения.

  • Равенство используется операторами равенства (=, <>) и оператором членства в списке (IN). Оно определяет базовую семантику, чтобы установить, являются ли два значения одинаковыми в этих контекстах. Равенство также неявно используется ассоциативными массивами констант в шаблонах узлов и отношений, поскольку такие массивы представляют собой просто сокращённое обозначение предикатов равенства.

  • Упорядочиваемость используется предложением ORDER BY и определяет базовую семантику того, как упорядочивать значения.

  • Эквивалентность используется модификатором DISTINCT и группировкой в предложениях проекций (WITH, RETURN) и определяет базовую семантику, позволяющую установить, являются ли два значения в этих контекстах одинаковыми.

F.3.5.2. Сопоставимость и равенство

Операторы сравнения должны работать так, как и ожидается — равенство и сопоставимость. Но в то же время им необходимо разрешить сортировку данных в столбцах — эквивалентность и упорядочиваемость.

К сожалению, в Postgres Pro может оказаться невозможным реализовать отдельные операторы сравнения для операций сравнения и равенства, а также операций эквивалентности и упорядочиваемости для одного и того же запроса. Поэтому в apache_age отдаётся приоритет эквивалентности и упорядочиваемости над равенством и сопоставимостью, чтобы обеспечить упорядочивание выходных данных.

F.3.5.2.1. Сопоставимость

Сопоставимость определяется между любой парой значений, как указано ниже.

  • Числа

    • Числа разных типов (за исключением значений NaN и бесконечностей) сравниваются друг с другом, как если бы оба числа были приведены к типу bigdecimal произвольной точности (в настоящее время вне системы типов Cypher), прежде чем сравниваться в порядке возрастания числовых значений.

    • Сравнение с любым значением, которое не является числом, следует правилам упорядочиваемости.

    • Числа с плавающей точкой не обладают необходимой точностью для представления всех целых чисел в диапазонах agtype integer и agtype numeric. При приведении значений типа agtype integer или agtype numeric в верхнем и нижнем диапазоне к типу float могут возникнуть непредвиденные результаты.

    • Целые числа (integer)

      • Целые числа сравниваются в порядке возрастания числовых значений.

    • Числа с плавающей точкой (float)

      • Числа с плавающей точкой (за исключением значений NaN и бесконечностей) сравниваются в порядке возрастания числовых значений.

      • Положительная бесконечность имеет тип FLOAT, равна самой себе и больше любого другого числа, кроме значений NaN.

      • Отрицательная бесконечность имеет тип FLOAT, равна самой себе и меньше любого другого числа.

      • Значения NaN сопоставимы друг с другом и превышают любое другое значение с плавающей точкой.

    • Числа (numeric)

      • Числа сравниваются в порядке возрастания числовых значений.

  • Логические значения (booleans)

    • Логические значения сравниваются таким образом, что значение false считается меньше, чем true.

    • Сравнение с любым значением, которое не является логическим, следует правилам упорядочиваемости.

  • Строки (string)

    • Строки сравниваются в словарном порядке, т. е. символы сравниваются попарно в порядке возрастания от начала строки к концу. Символы, отсутствующие в более короткой строке, считаются меньше, чем любой другой символ. Например, 'a' < 'аа'.

    • Сравнение с любым значением, которое не является строкой, следует правилам упорядочиваемости.

  • Списки (list)

    • Списки сравниваются в последовательном порядке, т. е. элементы списка сравниваются попарно в порядке возрастания от начала списка к концу. Элементы, отсутствующие в более коротком списке, считаются меньшими, чем любое другое значение (включая значения null), например, [1] < [1, 0], но также [1] < [1, null].

    • Сравнение с любым значением, которое не является списком, следует правилам упорядочиваемости.

  • Ассоциативные массивы (map)

    • Порядок сравнения ассоциативных массивов не определён и зависит от конкретной реализации.

    • Порядок сравнения ассоциативных массивов должен соответствовать семантике равенства, описанной ниже. Следовательно, любой ассоциативный массив, содержащий запись, которая сопоставляет свой ключ со значением null, является несравниваемым. Например, {a: 1} <= {a: 1, b: null} имеет значение null.

    • Сравнение с любым значением, которое не является обычным ассоциативным массивом, следует правилам упорядочиваемости.

Сущности:

  • Вершины: порядок сравнения вершин основан на присвоенном graphid.

  • Рёбра: порядок сравнения рёбер основан на присвоенном graphid.

  • Пути: пути сравниваются так, как если бы они были списком чередующихся узлов и отношений пути от начального узла к конечному узлу. Например, если есть узлы n1, n2, n3 и отношения r1 и r2, а n1 < n2 < n3 и r1 < r2, путь p1 от n1 до n3 через r1 будет меньше пути p2 в n1 от n2 через r2. Пути выражаются в виде списков:

    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

    Примечание

    Сравнение с любым значением, которое не является путём, вернёт false.

  • NULL: null не сравним ни с каким другим значением (включая другие значения null).

F.3.5.3. Упорядочиваемость между различными типами agtype

Упорядочивание различных типов agtype при использовании <, <=, >, >= от наименьшего значения к наибольшему:

  1. Путь

  2. Ребро

  3. Вершина

  4. Объект

  5. Массив

  6. Строка

  7. Логическое значение

  8. Numeric, Integer, Float

  9. NULL

Примечание

Это может измениться в будущих выпусках.

F.3.6. Операторы

F.3.6.1. Операторы сравнения строк

SELECT * FROM cypher('graph_name', $$
CREATE (:Person {name: 'John'}),
       (:Person {name: 'Jeff'}),
       (:Person {name: 'Joan'}),
       (:Person {name: 'Bill'})
$$) AS (result agtype);
STARTS WITH

Выполняет поиск префиксов по строкам с учётом регистра.

SELECT * FROM cypher('graph_name', $$
	MATCH (v:Person)
	WHERE v.name STARTS WITH "J"
	RETURN v.name
$$) AS (names agtype);

 names
--------
 "John"
 "Jeff"
 "Joan"
(3 rows)
CONTAINS

Выполняет поиск включений по строкам с учётом регистра.

SELECT * FROM cypher('graph_name', $$
	MATCH (v:Person)
	WHERE v.name CONTAINS "o"
	RETURN v.name
$$) AS (names agtype);

 names
--------
 "John"
 "Joan"
(2 rows)
ENDS WITH

Выполняет поиск суффиксов по строкам с учётом регистра.

SELECT * FROM cypher('graph_name', $$
	MATCH (v:Person)
	WHERE v.name ENDS WITH "n"
	RETURN v.name
$$) AS (names agtype);

 names
--------
 "John"
 "Joan"
(2 rows)
F.3.6.1.1. Регулярные выражения

apache_age поддерживает использование регулярных выражений POSIX с применением оператора =~. По умолчанию =~ чувствителен к регистру.

Простое соответствие строк

Оператор =~ без указания специальных символов действует как оператор =.

SELECT * FROM cypher('graph_name', $$
	MATCH (v:Person)
	WHERE v.name =~ 'John'
	RETURN v.name
$$) AS (names agtype);

 names
--------
 "John"
(1 row)

Если добавить (?i) в начало строки, при сравнении не учитывается регистр.

SELECT * FROM cypher('graph_name', $$
	MATCH (v:Person)
	WHERE v.name =~ '(?i)JoHn'
	RETURN v.name
$$) AS (names agtype);

 names
--------
 "John"
(1 row)
Знак подстановки .

Оператор . действует как знак подстановки, соответствующий любому отдельному символу.

SELECT * FROM cypher('graph_name', $$
	MATCH (v:Person)
	WHERE v.name =~ 'Jo.n'
	RETURN v.name
$$) AS (names agtype);

 names
--------
 "John"
 "Joan"
(2 rows)
Знак подстановки *

Знак подстановки * соответствует 0 или более вхождениям символа перед этим знаком.

SELECT * FROM cypher('graph_name', $$
	MATCH (v:Person)
	WHERE v.name =~ 'Johz*n'
	RETURN v.name
$$) AS (names agtype);

names
--------
 "John"
(1 row)
Оператор +

Оператор + соответствует одному предыдущему символу или более.

SELECT * FROM cypher('graph_name', $$
	MATCH (v:Person)
	WHERE v.name =~ 'Bil+'
	RETURN v.name
$$) AS (names agtype);

 names
--------
 "Bill"
(1 row)
Совместное использование знаков подстановки . и *

Можно использовать подстановочные знаки . и * вместе, чтобы заменить оставшуюся часть строки.

SELECT * FROM cypher('graph_name', $$
	MATCH (v:Person)
	WHERE v.name =~ 'J.*'
	RETURN v.name
$$) AS (names agtype);

 names
--------
 "John"
 "Jeff"
 "Joan"
(3 rows)

F.3.6.2. Приоритет операторов

Приоритет операторов в apache_age представлен ниже:

Таблица F.5. Приоритет операторов

ПриоритетОператорОписание
1.Обращение к свойству
2[]Обращение по индексу к ассоциативным массивам и спискам
 ()Вызов функций
3STARTS WITHПоиск префиксов по строкам с учётом регистра
 ENDS WITHПоиск суффиксов по строкам с учётом регистра
 CONTAINSПоиск включений по строкам регистра в строках
 =~Соответствие строке регулярного выражения
4-Унарный минус
5INПроверка существования элемента в списке
 IS NULLЯвляется ли значение NULL
 IS NOT NULLОтличается ли значение от NULL
6^Возведение в степень
7* / %Умножение, деление и остаток
8+ -Сложение и вычитание
9= <>Для реляционных = и ≠ соответственно
 < <=Для реляционных < и ≤ соответственно
 > >=Для реляционных > и ≥ соответственно
10NOTЛогическое НЕ
11ANDЛогическое И
12ORЛогическое ИЛИ

F.3.7. Агрегирование

Обычно агрегирование aggr(expr) обрабатывает все совпадающие строки для каждого ключа агрегирования, найденного во входящей записи (ключи сравниваются с использованием эквивалентности).

При обычном агрегировании (т. е. в форме aggr(expr)) список агрегированных значений представляет собой список значений-кандидатов, из которого удалены все значения null.

SELECT * FROM cypher('graph_name', $$
    CREATE (a:Person {name: 'A', age: 13}),
    (b:Person {name: 'B', age: 33, eyes: "blue"}),
    (c:Person {name: 'C', age: 44, eyes: "blue"}),
    (d1:Person {name: 'D', eyes: "brown"}),
    (d2:Person {name: 'D'}),
    (a)-[:KNOWS]->(b),
    (a)-[:KNOWS]->(c),
    (a)-[:KNOWS]->(d1),
    (b)-[:KNOWS]->(d2),
    (c)-[:KNOWS]->(d2)
$$) as (a agtype);

F.3.7.1. Автоматическая группировка

Для вычисления агрегированных данных в Cypher предусмотрено агрегирование, аналогичное GROUP BY в SQL.

Агрегатные функции принимают набор значений и вычисляют по ним агрегированное значение. Например, avg() вычисляет среднее значение нескольких числовых значений, а min() находит наименьшее числовое или строковое значение в наборе значений. Ниже указывается, что агрегатная функция работает с набором значений, при этом имеется в виду, что они являются результатом применения внутреннего выражения (например, n.age) ко всем записям в пределах одной группы агрегирования.

Агрегирование можно выполнять по всем совпадающим подграфам или дополнительно разделять путём введения ключей группировки. Это неагрегатные выражения, которые используются для группировки значений, входящих в агрегатные функции.

Допустим, что у нас есть следующий оператор RETURN:

SELECT * FROM cypher('graph_name', $$
    MATCH (v:Person)
    RETURN v.name, count(*)
$$) as (grouping_key agtype, count agtype);

 grouping_key | count
--------------+-------
 "A"          | 1
 "D"          | 2
 "B"          | 1
 "C"          | 1
(4 rows)

У нас есть два возвращаемых выражения: grouping_key и count(*). Первое, grouping_key, не является агрегатной функцией и поэтому будет ключом группировки. Последнее, count(*), представляет собой агрегатное выражение. Соответствующие подграфы будут разделены на разные группы в зависимости от ключа группировки. Затем для этих групп будет запущена агрегатная функция, вычисляющая агрегированное значение для каждой группы.

F.3.7.2. Сортировка по агрегатным функциям

Чтобы для сортировки набора результатов использовать агрегирование, его следует включить в предложение RETURN, которое будет использоваться в ORDER BY.

SELECT *
FROM cypher('graph_name', $$
    MATCH (me:Person)-[]->(friend:Person)
    RETURN count(friend), me
    ORDER BY count(friend)
$$) as (friends agtype, me agtype);

F.3.7.3. Агрегирование с DISTINCT

В агрегировании DISTINCT (т. е. в форме aggr(DISTINCT expr)) список агрегированных значений представляет собой список значений-кандидатов, из которого удалены все значения null. Кроме того, при агрегировании DISTINCT только одно из всех эквивалентных значений-кандидатов включается в список агрегированных значений, т. е. дубликаты при эквивалентности удаляются.

Оператор DISTINCT работает совместно с агрегированием. Он используется для того, чтобы сделать все значения уникальными перед их использованием в агрегатной функции.

SELECT *
FROM cypher('graph_name', $$
    MATCH (v:Person)
    RETURN count(DISTINCT v.eyes), count(v.eyes)
$$) as (distinct_eyes agtype, eyes agtype);

 distinct_eyes | eyes
---------------+------
 2             | 3
(1 row)

F.3.7.4. Неоднозначная группировка

При использовании операторов, не требующих от пользователя указания ключей группировки для запроса, Cypher может неоднозначно воспринимать, что именно следует квалифицировать как ключ группировки.

SELECT * FROM cypher('graph_name', $$
CREATE (:L {a: 1, b: 2, c: 3}),
       (:L {a: 2, b: 3, c: 1}),
       (:L {a: 3, b: 1, c: 2})
$$) as (a agtype);
F.3.7.4.1. Недопустимые запросы в apache_age

В apache_age для столбца WITH или RETURN запрещается комбинировать переменные с агрегатными функциями, которые явно не указаны в другом столбце того же предложения WITH или RETURN.

SELECT * FROM cypher('graph_name', $$
    MATCH (x:L)
    RETURN x.a + count(*) + x.b + count(*) + x.c
$$) as (a agtype);

ERROR:  'x' must be either part of an explicitly listed key or used inside an aggregate function
LINE 3: RETURN x.a + count(*) + x.b + count(*) + x.c
F.3.7.4.2. Допустимые запросы в apache_age

Столбцы, которые не включают агрегатную функцию в apache_age, считаются ключами группировки для этого предложения WITH или RETURN.

Приведённый выше запрос можно переписать несколькими способами, которые вернут результаты.

SELECT * FROM cypher('graph_name', $$
    MATCH (x:L)
    RETURN (x.a + x.b + x.c) + count(*) + count(*), x.a + x.b + x.c
$$) as (count agtype, key agtype);

count  | key
-------+-----
 12    | 6
(1 row)

x.a + x.b + x.c — это ключ группировки. Созданные таким образом ключи группировки должны включать круглые скобки.

SELECT * FROM cypher('graph_name', $$
    MATCH (x:L)
    RETURN x.a + count(*) + x.b + count(*) + x.c, x.a, x.b, x.c
$$) as (count agtype, a agtype, b agtype, c agtype);

 count | a | b | c
-------+---+---+---
 10    | 3 | 1 | 2
 10    | 2 | 3 | 1
 10    | 1 | 2 | 3
(3 rows)

x.a, x.b и x.c считаются разными ключами группировки.

F.3.7.4.3. Вершины и рёбра в неоднозначной группировке

В качестве альтернативы ключом группировки может быть вершина или ребро, а затем любые свойства вершины или ребра могут быть заданы без явного указания в столбце WITH или RETURN.

SELECT * FROM cypher('graph_name', $$
    MATCH (x:L)
    RETURN count(*) + count(*) + x.a + x.b + x.c, x
$$) as (count agtype, key agtype);

 count |                                          key
-------+----------------------------------------------------------------------------------------
 8     | {"id": 1407374883553283, "label": "L", "properties": {"a": 3, "b": 1, "c": 2}}::vertex
 8     | {"id": 1407374883553281, "label": "L", "properties": {"a": 1, "b": 2, "c": 3}}::vertex
 8     | {"id": 1407374883553282, "label": "L", "properties": {"a": 2, "b": 3, "c": 1}}::vertex
(3 rows)

Результаты будут сгруппированы по x, поскольку можно предположить, что ненужные для группировки свойства являются однозначными.

F.3.7.4.4. Скрытие нежелательных ключей группировки

Если ключ группировки считается ненужным для вывода запроса, агрегирование можно выполнить в предложении WITH, а затем передать информацию в предложение RETURN.

SELECT * FROM cypher('graph_name', $$
    MATCH (x:L)
    WITH count(*) + count(*) + x.a + x.b + x.c as column, x
    RETURN column
$$) as (a agtype);

 a
---
 8
 8
 8
(3 rows)

F.3.8. Импорт графов из файлов

Создание графов из файлов описано в следующих разделах:

  • Функции для загрузки графов из файлов

  • Структура файлов CSV, которые загружают функции в качестве входных данных

  • Простой пример исходного кода для загрузки стран и городов из файлов

Примечание

Пользователь должен создать граф и метки перед загрузкой данных из файлов.

F.3.8.1. Функции для загрузки графа

Ниже приведены подробные сведения о функциях для загрузки вершин и рёбер из файла.

Функция load_labels_from_file используется для загрузки вершин из CSV-файлов.

load_labels_from_file('имя_графа',
                      'имя_метки',
                      'путь_к_файлу')

При добавлении четвёртого параметра пользователь может исключить поле id. Следует использовать, если в файле нет поля id.

load_labels_from_file('имя_графа',
                      'имя_метки',
                      'путь_к_файлу',
                      false)

Функцию load_edges_from_file можно использовать для загрузки рёбер из CSV-файла. Структура файла представлена ниже.

Примечание

Убедитесь, что идентификаторы в файле с описанием рёбер идентичны идентификаторам в файлах с описанием вершин.

load_edges_from_file('имя_графа',
                      'имя_метки',
                      'путь_к_файлу');

F.3.8.2. Пояснения к формату CSV

Ниже описывается структура CSV-файлов для вершин и рёбер.

CSV-файл для узлов должен иметь следующий вид:

Таблица F.6. Формат CSV-файла для узлов

Имя поляОписание поля
idПервый столбец файла, все значения в котором — положительные целые числа. Это необязательное поле, если id_field_exists имеет значение false. Однако оно должно присутствовать, если для id_field_exists не установлено значение false.
СвойстваВо всех остальных столбцах содержатся свойства узлов. Строка заголовка содержит имя свойства.

Подобным образом, CSV-файл для рёбер должен иметь следующий вид:

Таблица F.7. Формат CSV-файла для рёбер

Имя поляОписание поля
start_idИдентификатор узла, из которого начинается ребро. Этот идентификатор присутствует в файле nodes.csv.
start_vertex_typeКласс узла
end_idКонечный идентификатор узла, в котором заканчивается ребро.
end_vertex_typeКласс узла
propertiesСвойства ребра. Заголовок содержит имя свойства.

F.3.8.3. Пример SQL-скрипта

  • Загрузите apache_age и создайте граф.

    LOAD 'age';
    
    SET search_path TO ag_catalog;
    SELECT create_graph('agload_test_graph');
  • Создайте метку Country и загрузите вершины из CSV-файла. Обратите внимание, что в этом CSV-файле есть поле id.

    SELECT create_vlabel('agload_test_graph','Country');
    SELECT load_labels_from_file('agload_test_graph',
                                 'Country',
                                 '/age/regress/age_load/data/countries.csv');
  • Создайте метку City и загрузите вершины из CSV-файла. Обратите внимание, что в этом CSV-файле есть поле id.

    SELECT create_vlabel('agload_test_graph','City');
    SELECT load_labels_from_file('agload_test_graph',
                                 'City',
                                 '/age/regress/age_load/data/cities.csv');
  • Создайте метку has_city и загрузите рёбра из CSV-файла.

    SELECT create_elabel('agload_test_graph','has_city');
    SELECT load_edges_from_file('agload_test_graph', 'has_city',
         '/age/regress/age_load/data/edges.csv');
  • Проверьте, правильно ли загружен граф.

    SELECT table_catalog, table_schema, table_name, table_type
    FROM information_schema.tables
    WHERE table_schema = 'agload_test_graph';
    
    SELECT COUNT(*) FROM agload_test_graph."Country";
    SELECT COUNT(*) FROM agload_test_graph."City";
    SELECT COUNT(*) FROM agload_test_graph."has_city";
    
    SELECT COUNT(*) FROM cypher('agload_test_graph', $$MATCH(n) RETURN n$$) as (n agtype);
    SELECT COUNT(*) FROM cypher('agload_test_graph', $$MATCH (a)-[e]->(b) RETURN e$$) as (n agtype);
F.3.8.3.1. Создание вершин без поля идентификатора в файле
  • Создайте метку Country2 и загрузите вершины из CSV-файла. Обратите внимание, что в этом CSV-файле нет поля id.

    SELECT create_vlabel('agload_test_graph','Country2');
    SELECT load_labels_from_file('agload_test_graph',
                                 'Country2',
                                 '/age/regress/age_load/data/countries.csv',
                                 false);
  • Создайте метку City2 и загрузите вершины из CSV-файла. Обратите внимание, что в этом CSV-файле нет поля id.

    SELECT create_vlabel('agload_test_graph','City2');
    SELECT load_labels_from_file('agload_test_graph',
                                 'City2',
                                 '/age/regress/age_load/data/cities.csv',
                                 false);
  • Проверьте, правильно ли загружен граф, и сравните автоматически созданные идентификаторы и идентификаторы, выбранные из файлов.

    Метки Country и City были созданы при наличии поля id в файле.

    Метки Country2 и City2 были созданы при отсутствии поля id в файле.

    SELECT COUNT(*) FROM agload_test_graph."Country2";
    SELECT COUNT(*) FROM agload_test_graph."City2";
    
    SELECT id FROM agload_test_graph."Country" LIMIT 10;
    SELECT id FROM agload_test_graph."Country2" LIMIT 10;
    
    SELECT * FROM cypher('agload_test_graph', $$MATCH(n:Country {iso2 : 'BE'})
        RETURN id(n), n.name, n.iso2 $$) as ('id(n)' agtype, 'n.name' agtype, 'n.iso2' agtype);
    SELECT * FROM cypher('agload_test_graph', $$MATCH(n:Country2 {iso2 : 'BE'})
        RETURN id(n), n.name, n.iso2 $$) as ('id(n)' agtype, 'n.name' agtype, 'n.iso2' agtype);
    
    SELECT * FROM cypher('agload_test_graph', $$MATCH(n:Country {iso2 : 'AT'})
        RETURN id(n), n.name, n.iso2 $$) as ('id(n)' agtype, 'n.name' agtype, 'n.iso2' agtype);
    SELECT * FROM cypher('agload_test_graph', $$MATCH(n:Country2 {iso2 : 'AT'})
        RETURN id(n), n.name, n.iso2 $$) as ('id(n)' agtype, 'n.name' agtype, 'n.iso2' agtype);
    
    SELECT drop_graph('agload_test_graph', true);

F.3.9. MATCH

Предложение MATCH позволяет указывать шаблоны, которые запрос будет искать в базе данных. Это основной способ получения данных для использования в запросе.

Предложение WHERE часто следует за предложением MATCH, чтобы добавить пользовательские ограничения к соответствующим шаблонам для работы с возвращённым набором данных. Предикаты являются частью описания шаблона, и их не следует рассматривать как фильтр, применяемый только после завершения сопоставления. Это означает, что часть WHERE всегда следует объединять с предложением MATCH, которому она принадлежит.

MATCH может встречаться в начале запроса или далее, например после WITH. Если это первое предложение, элементы пока не связаны, и Cypher выстраивает поиск так, чтобы найти результаты, совпадающие с этим предложением и любыми соответствующими ему предикатами, которые указаны в любой части предложения WHERE. Вершины и рёбра, найденные в результате этого поиска, доступны как связанные элементы шаблона и могут использоваться для сопоставления шаблонов подграфов. Их также можно использовать в любых будущих предложениях, где Cypher будет использовать известные элементы, а затем находить новые неизвестные элементы.

Cypher является декларативным языком, поэтому обычно в самом запросе не определяется алгоритм, который будет использоваться для выполнения поиска. Предикаты в частях WHERE могут вычисляться перед сопоставлением с шаблоном, во время сопоставления с шаблоном или после обнаружения совпадения.

F.3.9.1. Базовый поиск вершин

F.3.9.1.1. Получение всех вершин

Если указать шаблон с одной вершиной и без меток, будут возвращены все вершины графа.

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

                                      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 rows)

Возвращает все вершины в базе данных.

F.3.9.1.2. Получение всех вершин с меткой

Получить все вершины с метками можно с помощью одного шаблона узла, в котором у вершины есть метка, указанная следующим образом:

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

         title
------------------------
'Wall Street'
'The American President'
(2 rows)

Возвращает все фильмы в базе данных.

F.3.9.1.4. Сопоставление с метками

Чтобы шаблон применялся только к меткам на вершинах, добавьте его к вершине в шаблоне, используя синтаксис меток.

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

    title
-------------
'Wall Street'
(1 row)

Возвращает любые вершины, связанные с Person «Oliver», у которых есть метка Movie.

F.3.9.2. Поиск по рёбрам

F.3.9.2.1. Исходящие рёбра

Чтобы вернуть направленные рёбра, можно использовать -> или <- для указания направления ребра.

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

    title
-------------
'Wall Street'
(1 row)

Возвращает любые вершины, соединённые с Person «Oliver» исходящим ребром.

F.3.9.2.2. Направленные рёбра и использование переменной

Если либо для фильтрации по свойствам ребра, либо для возвращения ребра требуется переменная, укажите её в рамках необходимого ребра или вершины.

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

  title
----------
'DIRECTED'
(1 row)

Возвращает тип каждого ребра, исходящего из вершины «Oliver».

F.3.9.2.3. Сопоставление по метке ребра

Если известна метка ребра для сопоставления, можно указать эту метку с двоеточием перед ней.

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

   actors_name
-----------------
'Charlie Sheen'
'Martin Sheen'
'Michael Douglas'
(3 rows)

Возвращает всех актёров, которые играли (ACTED_IN) в фильме «Wall Street».

F.3.9.2.4. Сопоставление по метке ребра с переменной

Чтобы одновременно использовать переменную для хранения ребра и нужную метку ребра, укажите их обе.

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

     role
--------------
'Gordon Gekko'
'Carl Fox'
'Bud Fox'
(3 rows)

Возвращает роли актёров, игравших (ACTED_IN) в фильме «Wall Street».

F.3.9.2.5. Несколько рёбер

Рёбра можно связать вместе для сопоставления с бесконечным количеством рёбер. Пользователи могут связывать вместе рёбра и вершины для сопоставления с конкретными шаблонами, если соблюдается базовый шаблон ()-[]-().

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);

    title	    |     name
--------------+--------------
'Wall Street' |	'Oliver Stone'
(1 row)

Возвращает фильм, в котором снимался «Charlie Sheen», и имя режиссёра этого фильма.

F.3.9.3. Рёбра переменной длины

Когда соединение между двумя вершинами имеет переменную длину, список рёбер, образующих соединение, можно вернуть, используя следующее соединение.

Вместо описания длинного пути с последовательностью описаний множества вершин и рёбер в шаблоне многие рёбра (и промежуточные вершины) можно описать, указав длину в описании ребра в шаблоне.

(u)-[*2]->(v)

Такой шаблон, описывающий направленный вправо путь из трёх вершин и двух рёбер, можно переписать так:

(u)-[]->()-[]->(v)

Также можно указать длину диапазона:

(u)-[*3..5]->(v)

Что эквивалентно следующему:

(u)-[]->()-[]->()-[]->(v) and
(u)-[]->()-[]->()-[]->()-[]->(v) and
(u)-[]->()-[]->()-[]->()-[]->()-[]->(v)

В приведённом выше примере для пути задавалась как нижняя, так и верхняя граница количества рёбер (и вершин) между u и v. Можно исключить одно или оба эти значения связывания.

(u)-[*3..]->(v)

Возвращает все пути между u и v, включающие три или более рёбер.

(u)-[*..5]->(v)

Возвращает все пути между u и v, включающие пять или менее рёбер.

(u)-[*]->(v)

Возвращает все пути между u и v.

F.3.9.3.1. Пример
SELECT * FROM cypher('graph_name', $$
    MATCH p = (actor {name: 'Willam Dafoe'})-[:ACTED_IN*2]-(co_actor)
    RETURN relationships(p)
$$) as (r agtype);

                                                                               r
------------------------------------------------------------------------------------------------------------------------------------------------------------------
[{id: 0; label:"ACTED_IN"; properties: {role: "Green Goblin"}}::edge, {id: 1; label: "ACTED_IN; properties: {role: "Spiderman", actor: "Toby Maguire}}::edge]
[{id: 0; label:"ACTED_IN"; properties: {role: "Green Goblin"}}::edge, {id: 2; label: "ACTED_IN; properties: {role: "Spiderman", actor: "Andrew Garfield"}}::edge]
(2 rows)

Возвращает список рёбер, относящихся к ролям «Willam Dafoe» и актёрам, с которыми он работал.

F.3.10. WITH

Используя WITH, можно работать с выводом, прежде чем он будет передан в следующие части запроса. Например, можно изменять форму и/или количество записей в наборе результатов.

В WITH и RETURN можно задавать псевдонимы выражений, используемые в качестве имени связывания.

WITH также используется, чтобы отделить чтение графа от его изменения. Каждая часть запроса должна быть доступна либо только для чтения, либо только для записи. Чтобы перейти от части записи к части чтения, используйте необязательное предложение WITH.

F.3.10.1. Фильтр результатов агрегатной функции

Чтобы можно было фильтровать агрегированные результаты, они должны пройти через предложение WITH.

SELECT *
FROM cypher('graph_name', $$
    MATCH (david {name: 'David'})-[]-(otherPerson)-[]->()
    WITH otherPerson, count(*) AS foaf
    WHERE foaf > 1
    RETURN otherPerson.name
$$) as (name agtype);

  name
--------
"Anders"
(1 row)

Запрос вернёт имя человека, связанного с «David» более чем одним исходящим отношением.

F.3.10.2. Сортировка результатов перед использованием функции collect

Можно отсортировать результаты перед их передачей в collect, таким образом, получившийся список тоже будет отсортирован.

SELECT *
FROM cypher('graph_name', $$
    MATCH (n)WITH n
    ORDER BY n.name DESC LIMIT 3
    RETURN collect(n.name)
$$) as (names agtype);

         names
-------------------------
["Emil","David","Ceasar"]
(1 row)

Возвращается список из не более чем трёх имён людей в обратном порядке.

F.3.11. RETURN

В части RETURN запроса определяется, какие части шаблона нужно вывести. Вывод может включать значения типа agtype, узлы, связи или свойства.

F.3.11.1. Вывод узлов

Чтобы вернуть узел, укажите его в операторе RETURN.

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

                       n
---------------------------------------------------
{id: 0; label: '' properties: {name: 'B'}}::vertex
(1 row)

В примере возвращается узел.

F.3.11.2. Вывод рёбер

Чтобы вернуть n рёбер, просто включите их в список RETURN.

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

                              r
-------------------------------------------------------------------
 {id: 2; startid: 0; endid: 1; label: 'KNOWS' properties: {}}::edge
(1 row)

В примере возвращается отношение.

F.3.11.3. Получение свойств

Чтобы вернуть свойство, используйте разделитель-точку:

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

 name
------
 'A'
(1 row)

Возвращается значение name свойства.

F.3.11.4. Получение всех элементов

Если необходимо вернуть все вершины, рёбра и пути, соответствующие запросу, можно использовать символ *.

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

                                             a                                                          |                                                                 b	                                                          |                                 r
--------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------
 {"id": 281474976710659, "label": "", "properties": {"age": 55, "name": "A", "happy": "Yes!"}}::vertex	| {"id": 1125899906842625, "label": "BLOCKS", "end_id": 281474976710660, "start_id": 281474976710659, "properties": {}}::edge	| {"id": 281474976710660, "label": "", "properties": {"name": "B"}}::vertex
 {"id": 281474976710659, "label": "", "properties": {"age": 55, "name": "A", "happy": "Yes!"}}::vertex	| {"id": 1407374883553281, "label": "KNOWS", "end_id": 281474976710660, "start_id": 281474976710659, "properties": {}}::edge	| {"id": 281474976710660, "label": "", "properties": {"name": "B"}}::vertex
(2 rows)

Возвращает две вершины и ребро, указанные в запросе.

F.3.11.5. Переменные с необычными символами

Чтобы ввести заполнитель, состоящий из символов, которых нет в английском алфавите, переменную можно заключить в `, например:

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);

 happy
-------
 "Yes!"
(1 row)

Возвращается узел с именем «A».

F.3.11.6. Псевдоним поля

Если имя поля должно отличаться от используемого выражения, можно переименовать его, изменив имя в определении списка столбцов.

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

 objects_name
-------------
 'A'
(1 row)

Возвращает свойство узла, но переименовывает поле.

F.3.11.7. Необязательные свойства

Если неизвестно, существует ли свойство, будет считаться, что оно имеет значение null, если оно отсутствует.

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

 age
-----
 55
 NULL
(2 rows)

В этом запросе возвращается свойство, если оно существует, или null в противном случае.

F.3.11.8. Прочие выражения

В качестве возвращаемого элемента можно использовать любое выражение — литералы, предикаты, свойства, функции и всё прочее.

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);

 older_than_30 | literal         | id
---------------+-----------------+----
 true          | 'I'm a literal' | 1
(1 row)

Возвращает предикат, литерал и вызов функции с параметром выражения шаблона.

F.3.11.9. Уникальные результаты

DISTINCT извлекает только уникальные записи в зависимости от полей, выбранных для вывода.

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

                          b
----------------------------------------------------
 {id: 1; label: '' properties: {name: 'B'}}::vertex
(1 row)

Запрос возвращает узел с именем «B», но только один раз.

F.3.12. ORDER BY

ORDER BY — это вложенное предложение, следующее за WITH и указывающее, что вывод должен быть отсортирован и как именно.

Обратите внимание, что можно сортировать только свойства узлов и отношений, но не сами узлы или отношения. В ORDER BY используется сравнение для сортировки вывода. За дополнительной информацией обратитесь к описанию упорядочивания и сравнения значений.

В зависимости от того, является ли проецируемое предложение RETURN или WITH агрегирующим или DISTINCT, действуют различные правила доступности переменных. Если эта проекция агрегирующая или DISTINCT, доступны только переменные, доступные в проекции. Если проекция не меняет количество результатов (в отличие от агрегирования и DISTINCT), доступны также переменные, указанные до предложения проецирования. Если проецируемое предложение скрывает уже существующие переменные, доступны только новые переменные.

Не разрешается использовать агрегатные выражения во вложенном предложении ORDER BY, если они также не перечислены в проецируемом предложении. Последнее правило предназначено для того, чтобы убедиться, что ORDER BY изменяет не результаты, а только их порядок.

F.3.12.1. Упорядочивание узлов по свойству

ORDER BY используется для сортировки вывода.

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);

  name  | age
--------+-----
  "A"	  | 34
  "B"	  | 34
  "C"	  | 32
(3 rows)

Возвращаются узлы, отсортированные по имени.

F.3.12.2. Упорядочивание узлов по нескольким свойствам

Можно упорядочить значения по нескольким свойствам, указав каждую переменную в предложении ORDER BY. Cypher отсортирует результаты по первой указанной переменной и, если значения равны, перейдёт к следующему свойству в предложении ORDER BY и так далее.

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);

  name  | age
--------+-----
  "C"	  | 32
  "A"	  | 34
  "B"	  | 34
(3 rows)

Возвращает узлы, отсортированные сначала по свойству возраста, а затем по имени.

F.3.12.3. Упорядочивание узлов в порядке убывания

Если добавить DESC[ENDING] после переменной для сортировки, сортировка будет выполнена в обратном порядке.

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);

  name  | age
--------+-----
  "C"	  | 32
  "B"	  | 34
  "A"	  | 34
(3 rows)

В примере возвращаются узлы, отсортированные по имени в обратном порядке.

F.3.12.4. Упорядочивание null

При сортировке набора результатов по возрастанию значение null всегда будет находиться в конце набора результатов, а при сортировке по убыванию — в начале.

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

  name  | age | height
--------+-----+--------
  "A"	  | 34  | 170
  "C"	  | 32  | 185
  "B"	  | 34  | NULL
(3 rows)

Узлы возвращаются отсортированными по свойству длины, причём последним является узел без этого свойства.

F.3.13. SKIP

SKIP определяет, с какой записи начинать включение записей в вывод.

При использовании SKIP набор результатов будет усечён сверху. Обратите внимание, что порядок вывода результатов — произвольный, если в запросе не указано предложение ORDER BY. SKIP принимает любое выражение, результатом которого является положительное целое число.

F.3.13.1. Пропуск первых трёх строк

Чтобы вернуть подмножество строк из результата, начиная сверху, используйте следующий синтаксис:

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

 names
-------
 "D"
 "E"
(2 rows)

Возвращается свойство имени соответствующего узла n, с ограничением 3.

F.3.13.2. Получение двух средних строк

Чтобы вернуть подмножество строк из результата, начиная с середины, используйте следующий синтаксис:

SELECT *
FROM cypher('graph_name', $$
    MATCH (n)
    RETURN n.name
    ORDER BY n.name
    SKIP 1
    LIMIT 2
$$) as (names agtype);

 names
-------
 "B"
 "C"
(2 rows)

Возвращаются две вершины из середины.

F.3.13.3. Использование выражения с SKIP для возвращения подмножества строк

Использование выражения с SKIP для возвращения подмножества строк.

SELECT *
FROM cypher('graph_name', $$
    MATCH (n)
    RETURN n.name
    ORDER BY n.name
    SKIP (3 * rand())+ 1
$$) as (a agtype);

 names
-------
 "C"
 "D"
 "E"
(3 rows)

Первые две вершины пропускаются, и в результате возвращаются только последние три.

F.3.14. LIMIT

LIMIT ограничивает количество записей в выводе.

LIMIT принимает любое выражение, результатом которого является положительное целое число.

F.3.14.1. Получение подмножества строк

Чтобы вернуть подмножество строк из результата, начиная сверху, используйте следующий синтаксис:

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

names
"A"
"B"
"C"
3 rows

Возвращается узел, и для него не существует свойство age.

F.3.14.2. Использование выражения с LIMIT для возвращения подмножества строк

LIMIT принимает любое выражение, результатом которого является положительное целое число, если это выражение не ссылается на какие-либо внешние переменные:

SELECT *
FROM cypher('graph_name', $$
    MATCH (n)
    RETURN n.name
    ORDER BY n.name
    LIMIT toInteger(3 * rand()) + 1
$$) as (names agtype);

 names
-------
 "A"
 "B"
(2 rows)

Возвращает от одного до трёх верхних элементов.

F.3.15. CREATE

Предложение CREATE используется для создания вершин и рёбер графа.

F.3.15.1. Завершающее предложение CREATE

Предложение CREATE, за которым не следует другое предложение, является завершающим предложением. Когда Cypher-запрос заканчивается завершающим предложением, результаты вызова функции Cypher не возвращаются. Однако вызов Cypher-функции по-прежнему требует определения списка столбцов. Когда Cypher-запрос заканчивается завершающим узлом, задайте одно значение в определении списка столбцов: в этой переменной не будут возвращаться данные.

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

 a
---
(0 rows)

F.3.15.2. Создание одной вершины

Создать одну вершину можно с помощью следующего запроса.

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

 v
---
(0 rows)

Этот запрос ничего не возвращает.

F.3.15.3. Создание нескольких вершин

Можно создать нескольких вершин, разделив их запятыми.

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

   a
-------
(0 rows)

F.3.15.4. Создание вершины с меткой

Для добавления метки при создании вершины используйте следующий синтаксис.

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

   v
-------
(0 rows)

Этот запрос ничего не возвращает.

F.3.15.5. Создание вершины и добавление меток и свойств

Можно создать вершину сразу с метками и свойствами.

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

   n
-------
(0 rows)

Этот запрос ничего не возвращает.

F.3.15.6. Получение созданного узла

Можно создать и вернуть узел в рамках одного запроса, например:

SELECT *
FROM cypher('graph_name', $$
    CREATE (a {name: 'Andres'})
    RETURN a
$$) as (a agtype);

                                       a
--------------------------------------------------------------------------------
 {"id": 281474976710660, "label": "", "properties": {"name": "Andres"}}::vertex
(1 row)

Возвращается только что созданный узел.

F.3.15.7. Создание ребра между двумя узлами

Чтобы создать ребро между двумя вершинами, сначала нужно выполнить MATCH для двух вершин. После сопоставления узлов создайте между ними ребро.

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);

                              e
-----------------------------------------------------------------------
{id: 3; startid: 0, endid: 1; label: 'RELTYPE'; properties: {}}::edge
(1 row)

Запрос возвращает созданное ребро.

F.3.15.8. Создание ребра и установка свойств

Свойства рёбер устанавливаются так же, как при создании вершин. Обратите внимание, что значения могут быть любым выражением.

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);

                                          e
---------------------------------------------------------------------------------------------------
 {id: 3; startid: 0, endid: 1; label: 'RELTYPE'; properties: {name: 'Node A<->Node B'}}::edge
(1 row)

Запрос в примере возвращает только что созданное ребро.

F.3.15.9. Создание полного пути

При использовании CREATE и шаблона будут созданы все части шаблонов, которые ещё не находятся в области видимости.

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

                     p
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 [{"id": 281474976710661, "label": "", "properties": {"name": "Andres"}}::vertex, {"id": 1407374883553282, "label": "WORKS_AT", "end_id": 281474976710662, "start_id": 281474976710661, "properties": {}}::edge, {"id": 281474976710662, "label": "", "properties": {}}::vertex, {"id": 1407374883553281, "label": "WORKS_AT", "end_id": 281474976710662, "start_id": 281474976710663, "properties": {}}::edge, {"id": 281474976710663, "label": "", "properties": {"name": "Michael"}}::vertex]::path
(1 row)

Этот запрос одновременно создаёт три узла и два отношения, назначает шаблон переменной пути и возвращает этот шаблон.

F.3.16. DELETE

Предложение DELETE используется для удаления элементов графа — узлов, связей или путей.

Предложение DELETE, за которым не следует другое предложение, называется завершающим предложением. Когда Cypher-запрос заканчивается завершающим предложением, результаты вызова функции Cypher не возвращаются. Однако вызов Cypher-функции по-прежнему требует определения списка столбцов. Когда Cypher-запрос заканчивается завершающим узлом, задайте одно значение в определении списка столбцов: в этой переменной не будут возвращаться данные.

Подробное описание удаления свойств находится в Подразделе F.3.17.4.

Можно удалить узел, не удаляя рёбра, которые начинаются или заканчиваются в указанной вершине. Либо удалите вершины явно, либо используйте DETACH DELETE.

F.3.16.1. Удаление изолированных вершин

Для удаления вершины используйте предложение DELETE.

SELECT *
FROM cypher('graph_name', $$
    MATCH (v:Useless)
    DELETE v
$$) as (v agtype);

   v
-------
(0 rows)

В результате такого запроса удалятся вершины (с меткой «Useless»), у которых нет рёбер. Этот запрос ничего не возвращает.

F.3.16.2. Удаление всех вершин и связанных с ними рёбер

В результате запроса с предложением MATCH собираются все узлы. Используйте параметр DETACH, чтобы сначала удалить рёбра вершины, а затем удалить саму вершину.

SELECT *
FROM cypher('graph_name', $$
    MATCH (v:Useless)
    DETACH DELETE v
$$) as (v agtype);

   v
-------
(0 rows)

Этот запрос ничего не возвращает.

F.3.16.3. Удаление только рёбер

Чтобы удалить ребро, используйте предложение MATCH для поиска нужных рёбер, а затем добавьте переменную в предложение DELETE.

SELECT *
FROM cypher('graph_name', $$
    MATCH (n {name: 'Andres'})-[r:KNOWS]->()
    DELETE r
$$) as (v agtype);

   v
-------
(0 rows)

Этот запрос ничего не возвращает.

F.3.16.4. Получение удалённой вершины

С помощью предложения RETURN можно вернуть вершины, которые были удалены.

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

                                  a
---------------------------------------------------------------------------
 {"id": 281474976710659, "label": "", "properties": {"name": "A"}}::vertex
(1 rows)

F.3.17. SET

Предложение SET используется для изменения меток и свойств вершин и рёбер.

F.3.17.1. Завершающие предложения SET

Предложение SET, за которым не следует другое предложение, является завершающим предложением. Когда Cypher-запрос заканчивается завершающим предложением, результаты вызова функции Cypher не возвращаются. Однако вызов Cypher-функции по-прежнему требует определения списка столбцов. Когда Cypher-запрос заканчивается завершающим узлом, задайте одно значение в определении списка столбцов: в этой переменной не будут возвращаться данные.

F.3.17.2. Установка свойства

Чтобы установить свойство узла или отношения, используйте SET.

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

   v
-------
(0 rows)

Запрос возвращает только что изменённый узел.

F.3.17.3. Получение созданной вершины

Создать одну вершину можно с помощью следующего запроса:

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

                                                  v
-----------------------------------------------------------------------------------------------------
 {id: 3; label: 'Person'; properties: {surname:"Taylor", name:"Andres", age:36, hungry:true}}::vertex
(1 row)

Запрос возвращает только что изменённую вершину.

F.3.17.4. Удаление свойства

Обычно свойство удаляется с помощью команды REMOVE, но иногда бывает удобно сделать это с помощью команды SET. Одним из примеров является ситуация, когда свойство получено из параметра.

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

                                         v
---------------------------------------------------------------------------------------
 {id: 3; label: 'Person'; properties: {surname:"Taylor", age:36, hungry:true}}::vertex
(1 row)

Запрос возвращает узел, и свойство name теперь отсутствует.

F.3.17.5. Задание нескольких свойств в одном предложении SET

Если нужно задать несколько свойств в рамках одного запроса, разделите их запятыми.

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

                                                    v
------------------------------------------------------------------------------------------------------------------------------
{"id": 281474976710661, "label": "", "properties": {"name": "Andres", "surname": "Taylor", "position": "Developer"}}: :vertex
(1 row)

F.3.18. REMOVE

Предложение REMOVE используется для удаления свойств вершин и рёбер.

Предложение REMOVE, за которым не следует другое предложение, называется завершающим предложением. Когда Cypher-запрос заканчивается завершающим предложением, результаты вызова функции Cypher не возвращаются. Однако вызов Cypher-функции по-прежнему требует определения списка столбцов. Когда Cypher-запрос заканчивается завершающим узлом, задайте одно значение в определении списка столбцов: в этой переменной не будут возвращаться данные.

В Cypher запрещается хранить null в свойствах. Если значение не существует, свойство отсутствует. Таким образом, удаление значения свойства узла или отношения также выполняется с помощью REMOVE.

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

                          andres
---------------------------------------------------------------
 {id: 3; label: 'Person'; properties: {name:"Andres"}}::vertex
(1 row)

Возвращается узел, и для него не существует свойство age.

F.3.19. MERGE

Предложение MERGE гарантирует наличие шаблона в графе. Шаблон либо уже существует, либо его необходимо создать.

MERGE либо сопоставляет существующие узлы, либо создаёт новые данные. Это сочетание предложений MATCH и CREATE.

Например, можно указать, что граф должен содержать узел для пользователя с определённым именем. Если узла с таким именем нет, будет создан новый узел с заданным свойством имени. При использовании MERGE для полных шаблонов либо должен совпадать весь шаблон, либо создаётся новый шаблон. MERGE не использует существующие шаблоны частично. Если необходимо использовать частичные совпадения, это можно сделать, разбив шаблон на несколько предложений MERGE.

Как и в случае с MATCH, MERGE может сопоставлять несколько вхождений шаблона. Если совпадений несколько, все они будут переданы на более поздние этапы запроса.

SELECT * from cypher('graph_name', $$
CREATE (A:Person {name: 'Charlie Sheen', bornIn: 'New York'}),
    (B:Person {name: 'Michael Douglas', bornIn: 'New Jersey'}),
    (C:Person {name: 'Rob Reiner', bornIn: 'New York'}),
    (D:Person {name: 'Oliver Stone', bornIn: 'New York'}),
    (E:Person {name: 'Martin Sheen', bornIn: 'Ohio'})
$$) as (result agtype);

F.3.19.1. MERGE для узлов

F.3.19.1.1. MERGE для узлов с меткой

Если указать шаблон с одной вершиной и без меток, будут возвращены все вершины графа.

SELECT * FROM cypher('graph_name', $$
MERGE (v:Critic)
RETURN v
$$) as (v agtype);

                    v
-------------------------------------------------
 {id: 0; label: 'Critic': properties:{}}::vertex
(1 row)

Если существует вершина с меткой «Critic», возвращается эта вершина. В противном случае вершина сначала создаётся, а затем возвращается.

F.3.19.1.2. MERGE для одной вершины со свойствами

Выполнение команды MERGE для узла вершины со свойствами, не все из которых соответствуют какой-либо существующей вершине.

SELECT * FROM cypher('graph_name', $$
MERGE (charlie {name: 'Charlie Sheen', age: 10})
RETURN charlie
$$) as (v agtype);

                                v
------------------------------------------------------------------------------
 {id: 0; label: 'Actor': properties:{name: 'Charlie Sheen', age: 10}}::vertex
(1 row)

Если существует вершина с меткой «Critic», возвращается эта вершина. В противном случае вершина сначала создаётся, а затем возвращается.

Если существует вершина со всеми свойствами, она возвращается. В противном случае создаётся новая вершина с именем «Charlie Sheen» и возвращается.

F.3.19.1.3. MERGE для одной вершины с указанием как метки, так и свойств

Объединение вершины, в которой ограничения метки и свойства соответствуют существующей вершине.

SELECT * FROM cypher('graph_name', $$
MERGE (michael:Person {name: 'Michael Douglas'})
RETURN michael.name, michael.bornIn
$$) as (Name agtype, BornIn agtype);

       name        |    bornin
-------------------+--------------
 "Michael Douglas" | "New Jersey"
(1 row)

«Michael Douglas» совпадает с существующей вершиной, и возвращаются свойства name и bornIn этой вершины.

F.3.20. Функции-предикаты

Предикаты — это логические функции, которые возвращают true или false для заданного набора входных данных. Чаще всего они используются для фильтрации подграфов в части WHERE запроса.

exists(property agtype) returns agtype boolean

Функция exists() возвращает true, если указанное свойство существует в узле, отношении или ассоциативном массиве. Её действие отличается от действия предложения EXISTS.

SELECT *
FROM cypher('graph_name', $$
     MATCH (n)
     WHERE exists(n.surname)
     RETURN n.first_name, n.last_name
$$) as (first_name agtype, last_name agtype);

 first_name |  last_name
------------+------------
   'John'	  |   'Smith'
   'Patty'	| 'Patterson'
(2 rows)
exists(path agtype) returns agtype boolean

Функция exists() возвращает true, если заданный путь уже существует.

SELECT *
FROM cypher('graph_name', $$
     MATCH (n)
     WHERE exists((n)-[]-({name: 'Willem Defoe'}))
     RETURN n.full_name
$$) as (full_name agtype);

  full_name
--------------
'Toby Maguire'
'Tom Holland'
(2 rows)

F.3.21. Скалярные функции

id(expression agtype) returns agtype integer

Функция id() возвращает идентификатор вершины или ребра.

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

 id
----
 0
 1
 2
 3
(4 rows)
start_id(expression agtype) returns agtype integer

Функция start_id() возвращает идентификатор начальной вершины ребра.

SELECT *
FROM cypher('graph_name', $$
    MATCH ()-[e]->()
    RETURN start_id(e)
$$) as (start_id agtype);

 start_id
----------
 0
 1
 2
 3
(4 rows)
end_id(expression agtype) returns agtype integer

Функция end_id() возвращает идентификатор конечной вершины ребра.

SELECT *
FROM cypher('graph_name', $$
    MATCH ()-[e]->()
    RETURN end_id(e)
$$) as (end_id agtype);

 end_id
--------
 4
 5
 6
 7
(4 rows)
type(edge agtype) returns agtype string

Функция type() возвращает тип ребра в виде строки.

SELECT *
FROM cypher('graph_name', $$
    MATCH ()-[e]->()
    RETURN type(e)
$$) as (type agtype);

 type
------
'KNOWS'
'KNOWS'
(2 rows)
properties(expression agtype) returns agtype map

Функция properties() возвращает значение типа agtype map, содержащее все свойства вершины или ребра. Если аргумент уже является ассоциативным массивом, он возвращается без изменений. Функция properties(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    CREATE (p:Person {name: 'Stefan', city: 'Berlin'})
    RETURN properties(p)
$$) as (type agtype);

                 type
--------------------------------------
 {"city": "Berlin", "name": "Stefan"}
(1 row)
head(list agtype) returns agtype

Функция head() возвращает первый элемент в списке типов. Функция head(null) возвращает null. Если первый элемент в списке — null, функция head(list) возвращает null.

SELECT *
FROM cypher('graph_name', $$
   MATCH (a)
   WHERE a.name = 'Eskil'
   RETURN a.array, head(a.array)
$$) as (lst agtype, lst_head agtype);

          lst          | lst_head
-----------------------+----------
 ["one","two","three"] | "one"
(1 row)
last(list agtype) returns agtype

Функция last() возвращает последний элемент в значении типа agtype list. Функция last(null) возвращает null. Если последний элемент в списке — null, функция last(list) возвращает null.

SELECT *
FROM cypher('graph_name', $$
MATCH (a)
WHERE a.name = 'Eskil'
RETURN a.array, last(a.array)
$$) as (lst agtype, lst_tail agtype);

          lst          | lst_tail
-----------------------+----------
 ["one","two","three"] | "three"
(1 row)
length(path agtype) returns agtype integer

Функция length() возвращает длину пути. Функция length(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
   MATCH p = (a)-[]->(b)-[]->(c)
   WHERE a.name = 'Alice'
   RETURN length(p)
$$) as (length_of_path agtype);

 length_of_path
----------------
 2
 2
 2
(3 rows)
size(list variadic "any") returns agtype integer

Функция size() возвращает длину списка. Функция size(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    RETURN size(['Alice', 'Bob'])
$$) as (size_of_list agtype);

 size_of_list
--------------
 2
(1 row)
startNode(edge agtype) returns agtype

Функция startNode() возвращает начальный узел ребра. Функция startNode(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    MATCH (x:Developer)-[r]-()
    RETURN startNode(r)
$$) as (v agtype);

                  v
------------------------------------------
Node[0]{name:"Alice",age:38,eyes:"brown"}
Node[0]{name:"Alice",age:38,eyes:"brown"}
(2 rows)
endNode(edge agtype) returns agtype

Функция endNode() возвращает конечный узел ребра. Функция endNode(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    MATCH (x:Developer)-[r]-()
    RETURN endNode(r)
$$) as (v agtype);

                  v
-------------------------------------------
Node[2]{name:"Charlie",age:53,eyes:"green"}
Node[1]{name:"Bob",age:25,eyes:"blue"}
(2 rows)
timestamp() returns agtype integer

Функция timestamp() возвращает разницу в миллисекундах между текущим временем и полуночью 1 января 1970 года по UTC. Функция timestamp возвращает одно и то же значение в течение всего запроса, даже для длительных запросов.

SELECT *
FROM cypher('graph_name', $$
    RETURN timestamp()
$$) as (t agtype);

       t
---------------
1613496720760
(1 row)
toBoolean(expression variadic "any") returns agtype boolean

Функция toBoolean() преобразует строковое значение в логическое значение. Функция toBoolean(null) возвращает null. Если выражение является логическим значением, оно возвращается без изменений. Если разбор завершается ошибкой, возвращается null.

SELECT *
FROM cypher('graph_name', $$
    RETURN toBoolean('TRUE'), toBoolean('not a boolean')
$$) as (a_bool agtype, not_a_bool agtype);

 a_bool | not_a_bool
--------+------------
 true   | NULL
(1 row)
toFloat(expression variadic "any") returns agtype float

Функция toFloat() преобразует целое число или строковое значение в число с плавающей точкой. Функция toFloat(null) возвращает null. Если выражение представляет собой число с плавающей точкой, оно возвращается без изменений. Если разбор завершается ошибкой, возвращается null.

SELECT *
FROM cypher('graph_name', $$
    RETURN toFloat('11.5'), toFloat('not a number')
$$) as (a_float agtype, not_a_float agtype);

 a_float | not_a_float
---------+-------------
 11.5    | NULL
(1 row)
toInteger(expression variadic "any") returns agtype integer

Функция toInteger() преобразует число с плавающей точкой или строковое значение в целочисленное значение. toInteger(null) возвращает null. Если выражение является целочисленным значением, оно возвращается без изменений. Если разбор завершается ошибкой, возвращается null.

SELECT *
FROM cypher('graph_name', $$
     RETURN toInteger('42'), toInteger('not a number')
$$) as (an_integer agtype, not_an_integer agtype);

 an_integer | not_an_integer
------------+----------------
 42         | NULL
(1 row)
coalesce(expression agtype [, expression agtype]*) returns agtype

Функция coalesce() возвращает первое значение, не являющееся null, из заданного списка выражений. Если все аргументы имеют значение null, возвращается значение null.

SELECT *
FROM cypher('graph_name', $$
MATCH (a)
WHERE a.name = 'Alice'
RETURN coalesce(a.hairColor, a.eyes), a.hair_color, a.eyes
$$) as (color agtype, hair_color agtype, eyes agtype);

 color | hair_color |  eyes
-------+------------+--------
“brown”| NULL	      | “Brown”
(1 row)

F.3.22. Функции списка

SELECT * from cypher('graph_name', $$
CREATE (A:Person {name: 'Alice', age: 38, eyes: 'brown'}),
    (B:Person {name: 'Bob', age: 25, eyes: 'blue'}),
    (C:Person {name: 'Charlie', age: 53, eyes: 'green'}),
    (D:Person {name: 'Daniel', age: 54, eyes: 'brown'}),
    (E:Person {name: 'Eskil', age: 41, eyes: 'blue', array: ['one', 'two', 'three']}),
    (A)-[:KNOWS]->(B),
    (A)-[:KNOWS]->(C),
    (B)-[:KNOWS]->(D),
    (C)-[:KNOWS]->(D),
    (B)-[:KNOWS]->(E)
$$) as (result agtype);
keys(expression agtype) returns agtype list

Функция keys() возвращает список, содержащий строковые представления всех имён свойств вершины, ребра или ассоциативного массива. Функция keys(null) возвращает null.

SELECT * from cypher('graph_name', $$
    MATCH (a)
    WHERE a.name = 'Alice'
    RETURN keys(a)
$$) as (result agtype);

         result
-------------------------
 ["age", "eyes", "name"]
(1 row)

Возвращается список, содержащий имена всех свойств вершины, привязанной к a.

range(start variadic "any", end variadic "any" [, step variadic "any"]) returns agtype list

Функция range() возвращает список, содержащий все целочисленные значения в диапазоне, который ограничен начальным значением start и конечным значением end, где разность step между любыми двумя последовательными значениями является постоянной. То есть возвращаемый список является арифметической прогрессией. Диапазон является инклюзивным, поэтому арифметическая прогрессия всегда будет содержать start и — в зависимости от значений start, step и endend.

SELECT *
FROM cypher('graph_name', $$
    RETURN range(0, 10), range(2, 18, 3)
$$) as (no_step agtype, step agtype);

              no_step               |         step
------------------------------------+-----------------------
 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] | [2, 5, 8, 11, 14, 17]
(1 row)

Возвращаются два списка чисел в заданных диапазонах.

labels(vertex agtype) returns agtype list

Функция labels() возвращает список, содержащий строковые представления всех меток узла. Функция labels(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    MATCH (a)
    WHERE a.name = 'Alice'
    RETURN labels(a)
$$) as (edges agtype);

   edges
------------
 ["Person"]
(1 row)

Возвращается список, содержащий все метки узла, привязанного к a.

nodes(path agtype) returns agtype list

Функция nodes() возвращает список, содержащий все вершины пути. Функция nodes(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    MATCH p = (a)-[]->(b)-[]->(c)
    WHERE a.name = 'Alice' AND c.name = 'Eskil'
    RETURN nodes(p)
$$) as (vertices agtype);

                                                                                                                                                                                     vertices
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 [{"id": 844424930131969, "label": "Person", "properties": {"age": 38, "eyes": "brown", "name": "Alice"}}::vertex, {"id": 844424930131970, "label": "Person", "properties": {"age": 25, "eyes": "blue", "name": "Bob"}}::vertex, {"id": 844424930131973, "label": "Person", "properties": {"age": 41, "eyes": "blue", "name": "Eskil", "array": ["one", "two", "three"]}}::vertex]
(1 row)

Возвращается список, содержащий все вершины пути p.

relationships(path agtype) returns agtype list

Функция relationships() возвращает список, содержащий все отношения пути. Функция relationships(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    MATCH p = (a)-[]->(b)-[]->(c)
    WHERE a.name = 'Alice' AND c.name = 'Eskil'
    RETURN relationships(p)
$$) as (edges agtype);

                                                                                                                          edges
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 [{"id": 1125899906842625, "label": "KNOWS", "end_id": 844424930131970, "start_id": 844424930131969, "properties": {}}::edge, {"id": 1125899906842629, "label": "KNOWS", "end_id": 844424930131973, "start_id": 844424930131970, "properties": {}}::edge]
(1 row)

Возвращается список, содержащий все рёбра пути p.

toBooleanList(list variadic "any") returns agtype list

Функция toBooleanList() преобразует список значений в список логических значений. Если какие-либо значения не могут быть преобразованы в логические значения, в возвращаемом списке они будут иметь значение null. Значения null и логические значения в списке сохраняются. Если список имеет значение null, возвращается null.

SELECT * FROM cypher('expr', $$
    RETURN toBooleanList(['true', 'false', 'true'])
$$) AS (toBooleanList agtype);

   toBooleanList
--------------------
[true, false, true]
(1 row)

F.3.23. Числовые функции

rand() returns agtype float

Функция rand() возвращает случайное число с плавающей точкой в диапазоне от 0 (включая) до 1 (исключая), то есть [0,1). Возвращаемые числа имеют приблизительно равномерное распределение.

SELECT *
FROM cypher('graph_name', $$
    RETURN rand()
$$) as (random_number agtype);

   random_number
-------------------
0.3586784748902053
(1 row)
abs(list variadic "any") returns agtype

Функция abs() возвращает абсолютное значение заданного числа. Функция abs(null) возвращает значение null. Если выражение отрицательное, возвращается -(выражение) (т. е. отрицательная форма выражения).

SELECT *
FROM cypher('graph_name', $$
    MATCH (a), (e) WHERE a.name = 'Alice' AND e.name = 'Eskil'
    RETURN a.age, e.age, abs(a.age - e.age)
$$) as (alice_age agtype, eskil_age agtype, difference agtype);

 alice_age | eskil_age | difference
-----------+-----------+------------
 38        | 41        | 3
(1 row)

Возвращается абсолютное значение разности значений age.

ceil(expression variadic "any") returns agtype float

Функция ceil() возвращает наименьшее число с плавающей точкой, которое больше или равно заданному числу и равно математическому целому числу. Функция ceil(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    RETURN ceil(0.1)
$$) as (ceil_value agtype);

 ceil_value
------------
 1.0
(1 row)

Возвращается округление 0.1 в большую сторону.

floor(expression variadic "any") returns agtype float

Функция floor() возвращает наибольшее число с плавающей точкой, которое меньше или равно заданному числу и равно математическому целому числу. Функция floor(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    RETURN floor(0.1)
$$) as (flr agtype);

 flr
-----
 0.0
(1 row)

Возвращается целая часть 0.1.

round(expression variadic "any") returns agtype float

Функция round() возвращает значение заданного числа, округлённое до ближайшего целого числа. round(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    RETURN round(3.141592)
$$) as (rounded_value agtype);

 rounded_value
---------------
 3.0
(1 row)
sign(expression variadic "any") returns agtype integer

Функция sign() возвращает знак данного числа: 0, если число равно 0, -1 для любого отрицательного числа и 1 для любого положительного числа. Функция sign(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    RETURN sign(-17), sign(0.1), sign(0)
$$) as (negative_sign agtype, positive_sign agtype, zero_sign agtype);

 negative_sign | positive_sign | zero_sign
---------------+---------------+-----------
 -1            | 1             | 0
(1 row)

Возвращаются знаки чисел -17 и 0.1.

F.3.24. Логарифмические функции

e() returns agtype float

Функция e() возвращает основание натурального логарифма, e.

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

         e
-------------------
 2.718281828459045
(1 row)
sqrt(expression variadic "any") returns agtype float

Функция sqrt() возвращает квадратный корень числа.

SELECT *
FROM cypher('graph_name', $$
    RETURN sqrt(144)
$$) as (results agtype);

 results
---------
 12.0
(1 row)
exp(expression variadic "any") returns agtype float

Функция exp() возвращает e^n, где e — основание натурального логарифма, а n — значение аргумента выражения. Функция exp(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    RETURN exp(2)
$$) as (e agtype);

        e
------------------
 7.38905609893065
(1 row)

Возвращается e в степени 2.

log(expression variadic "any") returns agtype float

Функция log() возвращает натуральный логарифм числа. Функции log(null) и log(0) возвращают null.

SELECT *
FROM cypher('graph_name', $$
    RETURN log(27)
$$) as (natural_logarithm agtype);

 natural_logarithm
-------------------
 3.295836866004329
(1 row)

Возвращается натуральный логарифм числа 27.

log10(expression variadic "any") returns agtype float

Функция log10() возвращает десятичный логарифм числа (по основанию 10). Функции log10(null) и log10(0) возвращают null.

SELECT *
FROM cypher('graph_name', $$
    RETURN log10(27)
$$) as (common_logarithm agtype);

  common_logarithm
--------------------
 1.4313637641589874
(1 row)

Возвращается десятичный логарифм числа 27.

F.3.25. Тригонометрические функции

degrees(expression variadic "any") returns agtype float

Функция grades() преобразует радианы в градусы. Функция degrees(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    RETURN degrees(3.14159)
$$) as (deg agtype);

        deg
-------------------
 179.9998479605043
(1 row)

Возвращается число градусов, примерно равное числу пи в радианах.

radians(expression variadic "any") returns agtype float

Функция radians() преобразует градусы в радианы. Функция radians(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    RETURN radians(180)
$$) as (rad agtype);

        rad
-------------------
 3.141592653589793
(1 row)

Возвращается число градусов, примерно равное числу пи в радианах.

pi() returns agtype float

Функция pi() возвращает математическую константу пи.

SELECT *
FROM cypher('graph_name', $$
    RETURN pi()
$$) as (p agtype);

         p
-------------------
 3.141592653589793
(1 row)

Возвращается константа пи.

sin(expression variadic "any") returns agtype float

Функция sin() возвращает синус числа. Функция sin(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    RETURN sin(0.5)
$$) as (s agtype);

         s
-------------------
 0.479425538604203
(1 row)

Возвращается синус 0.5.

cos(expression variadic "any") returns agtype float

Функция cos() возвращает косинус числа. Функция cos(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    RETURN cos(0.5)
$$) as (c agtype);

         c
--------------------
 0.8775825618903728
(1 row)

Возвращается косинус 0.5.

tan(expression variadic "any") returns agtype float

Функция tan() возвращает тангенс числа. Функция tan(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    RETURN tan(0.5)
$$) as (t agtype);

         t
--------------------
 0.5463024898437905
(1 row)

Возвращается тангенс 0.5.

cot(expression variadic "any") returns agtype float

Функция cot() возвращает котангенс числа. Функция cot(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    RETURN cot(0.5)
$$) as (t agtype);

         t
-------------------
 1.830487721712452
(1 row)

Возвращается котангенс 0.5.

asin(expression variadic "any") returns agtype float

Функция asin() возвращает арксинус числа. Функция asin(null) возвращает null. Если (выражение < -1) или (выражение > 1), то asin(expression) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    RETURN asin(0.5)
$$) as (arc_s agtype);

       arc_s
--------------------
 0.5235987755982989
(1 row)

Возвращается арксинус 0.5.

acos(expression variadic "any") returns agtype float

Функция acos() возвращает арксинус числа. Функция acos(null) возвращает null. Если (выражение < -1) или (выражение > 1), то acos(expression) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    RETURN acos(0.5)
$$) as (arc_c agtype);

       arc_c
--------------------
 1.0471975511965979
(1 row)

Возвращается арккосинус 0.5.

atan(expression variadic "any") returns agtype float

Функция atan() возвращает арктангенс числа. Функция atan(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    RETURN atan(0.5)
$$) as (arc_t agtype);

       arc_t
--------------------
 0.4636476090008061
(1 row)

Возвращается арктангенс 0.5.

atan2(expression1 variadic "any", expression2 variadic "any") returns agtype float

Функция atan() возвращает арктангенс набора координат в радианах. Функции atan2(null, null), atan2(null, expression2) и atan(expression1, null) возвращают null.

SELECT *
FROM cypher('graph_name', $$
    RETURN atan2(0.5, 0.6)
$$) as (arc_t2 agtype);

       arc_t2
--------------------
 0.6947382761967033
(1 row)

Возвращается арктангенс 0.5 и 0.6.

F.3.26. Строковые функции

replace(original, search variadic "any", replace variadic "any") returns agtype string

Функция replace() возвращает строку, в которой все вхождения первой указанной строки в исходной строке заменены другой указанной строкой. Если какой-либо аргумент имеет значение null, возвращается null. Если строка не нашлась в original, возвращается original.

SELECT *
FROM cypher('graph_name', $$
    RETURN replace('hello', 'l', 'w')
$$) as (str_array agtype);

 str_array
-----------
 "hewwo"
(1 row)
split(original variadic "any", split_delimiter variadic "any") returns list of agtype strings

Функция split() возвращает список строк, полученный в результате разделения исходной строки по совпадениям с заданным разделителем. Функции split(null, SplitDelimiter) и split(original, null) возвращают null.

SELECT *
FROM cypher('graph_name', $$
    RETURN split('one,two', ',')
$$) as (split_list agtype);

   split_list
----------------
 ["one", "two"]
(1 row)
left(original variadic "any", length variadic "any") returns agtype string

Функция left() возвращает строку, содержащую указанное число крайних левых символов исходной строки. Функции left(null, length) и left(null, null) возвращают null. Функция left(original, null) вызывает ошибку. Если length не является целым положительным числом, также возникает ошибка. Если length превышает размер original, возвращается original.

SELECT *
FROM cypher('graph_name', $$
    RETURN left('Hello', 3)
$$) as (new_str agtype);

 new_str
---------
 "Hel"
(1 row)
right(original variadic "any", length variadic "any") returns agtype string

Функция right() возвращает строку, содержащую указанное количество крайних правых символов исходной строки. Функции right(null, length) и right(null, null) возвращают null. Функция right(original, null) вызывает ошибку. Если length не является целым положительным числом, также возникает ошибка. Если length превышает размер parameter, возвращается original.

SELECT *
FROM cypher('graph_name', $$
    RETURN right('hello', 3)
$$) as (new_str agtype);

 new_str
---------
 "llo"
(1 row)
substring(original variadic "any", start variadic "any" [, length variadic "any"]) returns agtype string

Функция substring() возвращает подстроку из исходной строки по индексу с нумерацией от 0, от начала и до конца его длины. Параметр start использует индекс с нуля. Если length опущен, функция возвращает подстроку, начинающуюся с позиции, заданной start, и продолжающуюся до конца original. Если original имеет значение null, возвращается null. Если start или length имеет значение null или отрицательное целое число, возникает ошибка. Если start равен 0, подстрока начнётся с начала original. Если length равен 0, возвращается пустая строка.

SELECT *
FROM cypher('graph_name', $$
    RETURN substring('hello', 1, 3), substring('hello', 2)
$$) as (sub_str1 agtype, sub_str2 agtype);

 sub_str1 | sub_str2
----------+----------
 "ell"    | "llo"
(1 row)
rTrim(original variadic "any") returns agtype string

Функция rTrim() возвращает исходную строку с удалёнными конечными пробелами. Функция rTrim(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    RETURN rTrim(' hello ')
$$) as (right_trimmed_str agtype);

 right_trimmed_str
-------------------
 " hello"
(1 row)
lTrim(original variadic "any") returns agtype string

Функция lTrim() возвращает исходную строку с удалёнными начальными пробелами. Функция lTrim(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    RETURN lTrim(' hello ')
$$) as (left_trimmed_str agtype);

 left_trimmed_str
------------------
 "hello "
(1 row)
trim(original variadic "any") returns agtype string

Функция trim() возвращает исходную строку с удалёнными начальными и конечными пробелами. Функция trim(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    RETURN trim(' hello ')
$$) as (trimmed_str agtype);

 trimmed_str
-------------
 "hello"
(1 row)
toLower(original variadic "any") returns agtype string

Функция toLower() возвращает исходную строку в нижнем регистре. Функция toLower(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    RETURN toLower('HELLO')
$$) as (lower_str agtype);

 lower_str
-----------
 "hello"
(1 row)
toUpper(original variadic "any") returns agtype string

Функция toUpper() возвращает исходную строку в верхнем регистре. toUpper(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    RETURN toUpper('hello')
$$) as (upper_str agtype);

 upper_str
-----------
 "HELLO"
(1 row)
reverse(original variadic "any") returns agtype string

Функция reverse() возвращает строку с обратным порядком всех символов исходной строки. Функция reverse(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    RETURN reverse('hello')
$$) as (reverse_str agtype);

 reverse_str
-------------
 "olleh"
(1 row)
toString(expression agtype) returns string

Функция toString() преобразует значение типа integer, float или boolean в строку. Функция toString(null) возвращает null. Если передаваемое в expression выражение является строкой, оно возвращается без изменений.

SELECT *
FROM cypher('graph_name', $$
    RETURN toString(11.5),toString('a string'), toString(true)
$$) as (float_to_str agtype, str_to_str agtype, bool_to_string agtype);

 float_to_str | str_to_str | bool_to_string
--------------+------------+----------------
 "11.5"       | "a string" | "true"
(1 row)

F.3.27. Агрегатные функции

Функции, активирующие автоматическое агрегирование.

LOAD 'age';
SET search_path TO ag_catalog;

SELECT create_graph('graph_name');

SELECT * FROM cypher('graph_name', $$
    CREATE (a:Person {name: 'A', age: 13}),
    (b:Person {name: 'B', age: 33, eyes: 'blue'}),
    (c:Person {name: 'C', age: 44, eyes: 'blue'}),
    (d1:Person {name: 'D', eyes: 'brown'}),
    (d2:Person {name: 'D'}),
    (a)-[:KNOWS]->(b),
    (a)-[:KNOWS]->(c),
    (a)-[:KNOWS]->(d1),
    (b)-[:KNOWS]->(d2),
    (c)-[:KNOWS]->(d2)
$$) as (a agtype);
min(expression variadic "any") returns agtype

Функция min() возвращает минимальное значение в наборе значений. Любые значения null исключаются из вычисления. В наборе значений разных типов любое строковое значение всегда считается меньше, чем любое числовое значение, а любой список всегда считается меньше, чем любая строка. Списки сравниваются в словарном порядке, т. е. элементы списка сравниваются попарно в порядке возрастания от начала списка к концу. Функция min(null) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    MATCH (v:Person)
    RETURN min(v.age)
$$) as (min_age agtype);

 min_age
---------
 13
(1 row)

Возвращается наименьшее из всех значений свойства age.

Чтобы пояснить следующий пример, предположим, что сначала выполняются следующие три команды:

SELECT * FROM cypher('graph_name', $$
    CREATE (:min_test {val:'d'})
$$) as (result agtype);

SELECT * FROM cypher('graph_name', $$
    CREATE (:min_test {val:['a', 'b', 23]})
$$) as (result agtype);

SELECT * FROM cypher('graph_name', $$
    CREATE (:min_test {val:[1, 'b', 23]})
$$) as (result agtype);

В примере ниже показано использование функции min() со списками:

SELECT *
FROM cypher('graph_name', $$
    MATCH (v:min_test)
    RETURN min(v.val)
$$) as (min_val agtype);

    min_val
----------------
 ["a", "b", 23]
(1 row)

Возвращается наименьшее из всех значений в наборе (в данном случае список ["a", "b", 23]), поскольку два списка считаются меньшими значениями, чем строка «d», а строка «a» считается меньшим значением, чем числовое значение 1.

max(expression variadic "any") returns agtype float

Функция max() возвращает максимальное значение в наборе значений. Любые значения null исключаются из вычислений. В наборе значений разных типов любое строковое значение всегда считается меньше, чем любое числовое значение, а любой список всегда считается меньше, чем любая строка. Списки сравниваются в словарном порядке, т. е. элементы списка сравниваются попарно в порядке возрастания от начала списка к концу. Функция max(null) возвращает null.

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

 max_age
---------
 44
(1 row)

Возвращается наибольшее из всех значений свойства age.

stDev(expression variadic "any") returns agtype float

Функция stDev() возвращает стандартное отклонение для заданного значения по группе. Она применяет стандартный двухпроходный метод с N - 1 в качестве знаменателя и должна использоваться при выборке совокупности для получения несмещённой оценки. При расчёте стандартного отклонения для всей совокупности следует использовать stDevP. Любые значения null исключаются из вычисления. Функция stDev(null) возвращает 0.0 (ноль).

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

     stdev_age
--------------------
 15.716233645501712
(1 row)

Возвращается стандартное отклонение значений свойства age.

stDevP(expression variadic "any") returns agtype float

Функция stDev() возвращает стандартное отклонение для заданного значения по группе. Она применяет стандартный двухпроходный метод с N в качестве знаменателя и должна использоваться при расчёте стандартного отклонения для всей совокупности. Когда рассчитывается стандартное отклонение только выборки совокупности, следует использовать stDev. Любые значения null исключаются из вычисления. stDevP(null) возвращает 0.0 (ноль).

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

     stdevp_age
--------------------
 12.832251036613439
(1 row)

Возвращается стандартное отклонение совокупности значений свойства age.

percentileCont(expression agtype, percentile agtype) returns agtype float

Функция percentileCont() возвращает процентиль заданного значения для группы с процентилем от 0.0 до 1.0. Она использует метод линейной интерполяции, вычисляя средневзвешенное значение между двумя значениями, если желаемый процентиль находится между ними. Получить ближайшие значения с применением метода округления можно с помощью функции percentileDisc. Любые значения null исключаются из вычисления. Функция percentileCont(null, percentile) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    MATCH (n:Person)
    RETURN percentileCont(n.age, 0.4)
$$) as (percentile_cont_age agtype);

 percentile_cont_age
---------------------
 29.0
(1 row)

Возвращается 40-й процентиль значений свойства age, рассчитанный с использованием средневзвешенного значения. В данном случае 0.4 — это медиана или 40-й процентиль.

percentileDisc(expression agtype, percentile agtype) returns agtype float

Функция percentileDisc() возвращает процентиль заданного значения для группы с процентилем от 0.0 до 1.0. Она использует метод округления и вычисляет ближайшее значение к процентилю. Интерполированные значения можно получить с помощью функции percentileCont. Любые значения null исключаются из вычисления. Функция percentileDisc(null, percentile) возвращает null.

SELECT *
FROM cypher('graph_name', $$
    MATCH (n:Person)
    RETURN percentileDisc(n.age, 0.5)
$$) as (percentile_disc_age agtype);

 percentile_disc_age
---------------------
 33.0
(1 row)

Возвращается 50-й процентиль значений свойства age.

count(expression agtype) returns agtype integer

Функция count() возвращает количество значений или записей и существует в двух вариантах:

  • Функция count(*) возвращает число совпадающих записей.

  • Функция count(expr) возвращает количество возвращаемых выражением значений, не являющихся null.

Функция count(*) включает записи, возвращающие null. Функция count(expr) игнорирует значения null. Функция count(null) возвращает 0 (ноль). Функцию count(*) можно использовать для возвращения количества узлов: например, количества узлов, подключённых к некоторому узлу n.

SELECT *
FROM cypher('graph_name', $$
    MATCH (n {name: 'A'})-[]->(x)
    RETURN n.age, count(*)
$$) as (age agtype, number_of_people agtype);

 age | number_of_people
-----+------------------
 13  | 3
(1 row)

Возвращаются свойство age начального узла n (со значением имени «A») и количество узлов, связанных с n.

Функцию count(*) можно использовать для группировки и подсчёта типов отношений, она возвращает количество отношений каждого типа.

SELECT *
FROM cypher('graph_name', $$
    MATCH (n {name: 'A'})-[r]->()
    RETURN type(r), count(*)
$$) as (label agtype, count agtype);

  label  | count
---------+-------
 "KNOWS" | 3
(1 row)

Возвращаются тип связи и количество связей этого типа.

Вместо того, чтобы использовать функцию count(*) для обычного получения значений, может оказаться более полезным вернуть фактическое количество значений, возвращаемых выражением.

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

 count
-------
 3
(1 row)

Возвращается количество узлов, подключённых к начальному узлу n.

Функцию count(expression) можно использовать для получения количества выводимых выражением значений, не являющихся null.

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

 count
-------
 3
(1 row)

Возвращается количество узлов с меткой Person, которые имеют значение свойства age, не являющееся null.

В следующем примере мы пытаемся найти всех друзей наших друзей и посчитать их. Первая агрегатная функция, count(DISTINCT friends_of_friend), будет учитывать друг друга (friend_of_friend) только один раз, поскольку при указании DISTINCT удаляются дубликаты. Вторая агрегатная функция, count(friend_of_friend), будет рассматривать одного и то же friend_of_friend несколько раз.

SELECT *
FROM cypher('graph_name', $$
    MATCH (me:Person)-[]->(friend:Person)-[]->(friend_of_friend:Person)
    WHERE me.name = 'A'
    RETURN count(DISTINCT friend_of_friend), count(friend_of_friend)
$$) as (friend_of_friends_distinct agtype, friend_of_friends agtype);

 friend_of_friends_distinct | friend_of_friends
----------------------------+-------------------
 1                          | 2
(1 row)

Как B, так и C знают D, поэтому D будет учитываться дважды, если не использовать DISTINCT.

avg(expression agtype) returns agtype integer

Функция avg() возвращает среднее значение набора числовых значений. Любые значения null исключаются из вычисления. Функция avg(null) возвращает значение null.

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

 avg_age
---------
 30.0
(1 row)

Возвращается среднее значение всех свойств age.

sum(expression agtype) returns agtype float

Функция sum() возвращает сумму набора числовых значений. Любые значения null исключаются из вычисления. Функция sum(null) возвращает ноль.

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

 total_age
-----------
 90
(1 row)

Возвращается сумма всех значений свойства age.

F.3.28. Пользовательские функции

Пользователи могут добавлять собственные функции в apache_age. При использовании Cypher-функций все вызовы функций с Cypher-запросом используют пространство имён по умолчанию: ag_catalog. Однако если пользователь хочет использовать функцию за пределами этого пространства имён, нужно указать нужное пространство имён перед именем функции.

Синтаксис: имя_пространства_имён.имя_функции

SELECT *
FROM cypher('graph_name', $$
RETURN pg_catalog.sqrt(25)
$$) as (result agtype);

 result
--------
 25
(1 row)

F.3.29. apache_age за рамками языка Cypher

До сих пор все запросы выполнялись по одному и тому же шаблону: предложение SELECT, за которым следовал одиночный вызов Cypher в предложении FROM. Однако Cypher-запрос можно использовать многими другими способами. В этом разделе описываются некоторые более продвинутые способы вызова Cypher в более сложных гибридных запросах SQL/Cypher.

F.3.29.1. Использование Cypher в CTE

Нет никаких ограничений на использование Cypher с CTE (общими табличными выражениями).

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

 name      | age
-----------+-----
 "Andres"  | 36
 "Tobias"  | 25
 "Peter"   | 35
(3 rows)

F.3.29.2. Использование Cypher в выражениях JOIN

Cypher-запрос может быть частью предложения JOIN.

Примечание

Cypher-запросы с использованием предложений CREATE, SET, REMOVE нельзя использовать в SQL-запросах с JOIN, поскольку они влияют на систему транзакций Postgres Pro. Чтобы обойти данное ограничение, можно защитить запрос с помощью CTE.

SELECT id,
    graph_query.name = t.name as names_match,
    graph_query.age = t.age as ages_match
FROM schema_name.sql_person AS t
JOIN cypher('graph_name', $$
        MATCH (n:Person)
        RETURN n.name, n.age, id(n)
$$) as graph_query(name agtype, age agtype, id agtype)
ON t.person_id = graph_query.id;

 id | names_match | ages_match
 ---+-------------+------------
 1  | True        | True
 2  | False       | True
 3  | True        | False
(3 rows)

F.3.29.3. Cypher в SQL-выражениях

Cypher нельзя использовать в выражениях, запрос должен существовать в предложении FROM запроса. Однако если запрос Cypher используется в подзапросе, он будет действовать как любой запрос в стиле SQL.

F.3.29.3.1. Использование Cypher с =

При написании Cypher-запроса, возвращающего один столбец и одну строку, можно использовать оператор сравнения =.

SELECT t.name FROM schema_name.sql_person AS t
where t.name = (
    SELECT a
    FROM cypher('graph_name', $$
          MATCH (v)
        RETURN v.name
    $$) as (name varchar(50))
    ORDER BY name
    LIMIT 1);

   name   | age
----------+-----
 "Andres" | 36
(1 rows)
F.3.29.3.2. Работа с предложением IN Postgres Pro

При написании Cypher-запроса, возвращающего один столбец, но несколько строк, можно использовать оператор IN.

SELECT t.name, t.age FROM schema_name.sql_person as t
where t.name in (
    SELECT *
    FROM cypher('graph_name', $$
        MATCH (v:Person)
        RETURN v.name
    $$) as (a agtype));

  name     | age
-----------+-----
 "Andres"  | 36
 "Tobias"  | 25
 "Peter"   | 35
(3 rows)
F.3.29.3.3. Работа с предложением EXISTS Postgres Pro

При написании запроса Cypher, возвращающего более одного столбца и строки, можно использовать оператор EXISTS.

SELECT t.name, t.age
FROM schema_name.sql_person as t
WHERE EXISTS (
    SELECT *
    FROM cypher('graph_name', $$
      MATCH (v:Person)
        RETURN v.name, v.age
    $$) as (name agtype, age agtype)
    WHERE name = t.name AND age = t.age
);

   name    | age
-----------+-----
 "Andres"  | 36
 "Tobias"  | 25
 "Peter"   | 35
(3 rows)
F.3.29.3.4. Запрос к нескольким графам

В SQL-операторе можно обращаться к любому количеству графов одновременно. В запросе можно обращаться сразу к нескольким графам.

SELECT graph_1.name, graph_1.age, graph_2.license_number
FROM cypher('graph_1', $$
    MATCH (v:Person)
    RETURN v.name, v.age
$$) as graph_1(col_1 agtype, col_2 agtype, col_3 agtype)
JOIN cypher('graph_2', $$
    MATCH (v:Doctor)
    RETURN v.name, v.license_number
$$) as graph_2(name agtype, license_number agtype)
ON graph_1.name = graph_2.name

   name    | age | license_number
-----------+-----+----------------
 "Andres"  | 36  | 1234567890
(1 rows)

F.3.29.4. Подготовленные операторы

Cypher может выполнить запрос на чтение внутри подготовленного оператора. При использовании параметров с хранимыми процедурами SQL-параметр должен быть помещён в вызов функции Cypher. За более подробной информацией обратитесь к разделу Формат запросов apache_age.

Параметры в Cypher используются в таком формате: «$» и идентификатор. В отличие от параметров Postgres Pro, параметры Cypher начинаются с буквы, за которой следует буквенно-цифровая строка произвольной длины. Пример: $имя_параметра

Использование подготовленных операторов в Cypher является расширением системы хранимых процедур Postgres Pro. Используйте предложение PREPARE, чтобы создать запрос с вызовом функции Cypher. Не размещайте параметры стиля Postgres Pro в вызове Cypher-запроса, вместо этого поместите параметры Cypher в запрос, а параметр Postgres Pro — в качестве третьего аргумента в вызове функции Cypher.

PREPARE cypher_stored_procedure(agtype) AS
SELECT *
FROM cypher('expr', $$
    MATCH (v:Person)
    WHERE v.name = $name //параметр Cypher
    RETURN v
$$, $1) //An SQL Parameter must be placed in the Cypher function call
AS (v agtype);

При выполнении подготовленного оператора поместите значение типа agtype map со значениями параметров туда, где находится параметр Postgres Pro в вызове функции Cypher. Значение должно иметь тип agtype map, иначе возникнет ошибка, а также следует исключить $ в именах параметров.

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

F.3.29.5. Функции PL/pgSQL

Команды Cypher можно без ограничений выполнять в функциях PL/pgSQL.

SELECT *
FROM cypher('imdb', $$
    CREATE (toby:actor {name: 'Toby Maguire'}),
        (tom:actor {name: 'Tom Holland'}),
        (willam:actor {name: 'Willam Dafoe'}),
        (robert:actor {name: 'Robert Downey Jr'}),
        (spiderman:movie {title: 'Spiderman'}),
        (no_way_home:movie {title: 'Spiderman: No Way Home'}),
        (homecoming:movie {title: 'Spiderman: Homecoming'}),
        (ironman:movie {title: 'Ironman'}),
        (tropic_thunder:movie {title: 'Tropic Thunder'}),
        (toby)-[:acted_in {role: 'Peter Parker', alter_ego: 'Spiderman'}]->(spiderman),
        (willam)-[:acted_in {role: 'Norman Osborn', alter_ego: 'Green Goblin'}]->(spiderman),
        (toby)-[:acted_in {role: 'Toby Maguire'}]->(tropic_thunder),
        (robert)-[:acted_in {role: 'Kirk Lazarus'}]->(tropic_thunder),
        (robert)-[:acted_in {role: 'Tony Stark', alter_ego: 'Ironman'}]->(homecoming),
        (tom)-[:acted_in {role: 'Peter Parker', alter_ego: 'Spiderman'}]->(homecoming),
        (tom)-[:acted_in {role: 'Peter Parker', alter_ego: 'Spiderman'}]->(no_way_home),
        (toby)-[:acted_in {role: 'Peter Parker', alter_ego: 'Spiderman'}]->(no_way_home),
        (willam)-[:acted_in {role: 'Norman Osborn', alter_ego: 'Green Goblin'}]->(no_way_home)
$$) AS (a agtype);

Пример создания функции показан ниже.

CREATE OR REPLACE FUNCTION get_all_actor_names()
RETURNS TABLE(actor agtype)
LANGUAGE plpgsql
AS $BODY$
BEGIN
    LOAD 'age';
    SET search_path TO ag_catalog;

    RETURN QUERY
    SELECT *
    FROM ag_catalog.cypher('imdb', $$
        MATCH (v:actor)
        RETURN v.name
    $$) AS (a agtype);
END
$BODY$;

Выполните запрос:

SELECT * FROM get_all_actor_names();

       actor
--------------------
 "Toby Maguire"
 "Tom Holland"
 "Willam Dafoe"
 "Robert Downey Jr"
(4 rows)

Примечание

Рекомендуется добавить команду LOAD 'age' и задать search_path в объявлении функции, чтобы обеспечить согласованную работу команды CREATE FUNCTION.

F.3.29.5.1. Пример динамического Cypher-запроса
CREATE OR REPLACE FUNCTION get_actors_who_played_role(role agtype)
RETURNS TABLE(actor agtype, movie agtype)
LANGUAGE plpgsql
AS $function$
DECLARE sql VARCHAR;
BEGIN
        load 'age';
        SET search_path TO ag_catalog;

        sql := format('
        SELECT *
        FROM cypher(''imdb'', $$
            MATCH (actor)-[:acted_in {role: %s}]->(movie:movie)
            RETURN actor.name, movie.title
        $$) AS (actor agtype, movie agtype);
    ', role);

        RETURN QUERY EXECUTE sql;

END
$function$;
SELECT * FROM get_actors_who_played_role('"Peter Parker"');

     actor      |          movie
----------------+--------------------------
 "Toby Maguire" | "Spiderman: No Way Home"
 "Toby Maguire" | "Spiderman"
 "Tom Holland"  | "Spiderman: Homecoming"
 "Tom Holland"  | "Spiderman: No Way Home"
(4 rows)

F.3.29.6. SQL в Cypher

Расширение apache_age не поддерживает использование языка SQL непосредственно в Cypher-запросах. Однако с помощью пользовательских функций можно писать SQL-запросы и вызывать их командами Cypher.

Примечание

Это относится только к скалярным функциям и функциям void. Функции возвращения множеств в настоящее время не поддерживаются.

Создание функции:

CREATE OR REPLACE FUNCTION public.get_event_year(name agtype) returns agtype AS $$
    SELECT year::agtype
    FROM history AS h
    WHERE h.event_name = name::text
    LIMIT 1;
$$ LANGUAGE sql;
SELECT * FROM cypher('graph_name', $$
    MATCH (e:event)
    WHERE e.year < public.get_event_year(e.name)
    RETURN e.name
$$) as (n agtype);

        n
-------------------
 "Apache Con 2021"
(1 row)

F.3. apache_age

apache_age is a Postgres Pro extension that provides graph database functionality. AGE is an acronym for A Graph Extension. The goal of the project is to create single storage that can handle both relational and graph model data so that users can use standard ANSI SQL along with openCypher, the graph query language.

Important

User tables in apache_age databases contain columns using reg* OID-referencing system data types, therefore upgrading to a major version with pg_upgrade is not supported.

F.3.1. Installation and Setup

The apache_age extension is included into Postgres Pro Enterprise. Once you have Postgres Pro Enterprise installed, create the apache_age extension:

CREATE EXTENSION age;

For every connection of apache_age you start, load the apache_age library.

LOAD 'age';

Non-superusers must specify the full path to load the apache_age library.

LOAD '$libdir/plugins/age.so';

Add ag_catalog to the search_path to simplify queries:

SET search_path = ag_catalog, '$user', public;

In order to use apache_age, non-superusers need USAGE privileges on the ag_catalog schema (example for user db_user):

GRANT USAGE ON SCHEMA ag_catalog TO db_user;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA ag_catalog TO db_user;

F.3.2. 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.

F.3.2.1. Create a Graph

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

create_graph(graph_name text) returns void

This function will not return any results. The graph is created if there is no error message. Tables needed to set up the graph are created automatically.

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

F.3.2.2. Grant Privileges

As a superuser, you can grant privilege on a specific existing graph to a non-superuser (example for graph graph1 and user db_user):

GRANT USAGE ON SCHEMA graph1 TO db_user;
GRANT ALL PRIVILEGES ON SCHEMA graph1 TO db_user;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA graph1 TO db_user;
GRANT ALL PRIVILEGES ON TABLE graph1._ag_label_vertex TO db_user;

F.3.2.3. Delete a Graph

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

drop_graph(graph_name text, cascade boolean) returns void

This function will not return any results. If there is no error message the graph has been deleted. It is recommended to set the cascade option to true, otherwise everything in the graph must be manually dropped with SQL DDL commands.

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

F.3.2.4. How Graphs Are Stored In Postgres Pro

When creating graphs with apache_age, a Postgres Pro namespace will be generated for every individual graph. The name and namespace of the created graphs can be seen within the ag_graph table from the ag_catalog namespace:

SELECT create_graph('new_graph');

NOTICE:  graph 'new_graph' has been created
 create_graph
--------------

(1 row)

SELECT * FROM ag_catalog.ag_graph;

   name    | namespace
-----------+-----------
 new_graph | new_graph
(1 row)

After creating the graph, two tables are going to be created under the graph namespace to store vertices and edges: _ag_label_vertex and _ag_label_edge. These will be the parent tables of any new vertex or edge label. The query below shows how to retrieve the edge and vertex labels for all the graphs in the database.

-- Before creating a new vertex label.
SELECT * FROM ag_catalog.ag_label;

       name       | graph | id | kind |          relation          |        seq_name
------------------+-------+----+------+----------------------------+-------------------------
 _ag_label_vertex | 68484 |  1 | v    | new_graph._ag_label_vertex | _ag_label_vertex_id_seq
 _ag_label_edge   | 68484 |  2 | e    | new_graph._ag_label_edge   | _ag_label_edge_id_seq
(2 rows)

-- Creating a new vertex label.
SELECT create_vlabel('new_graph', 'Person');
NOTICE:  VLabel 'Person' has been created
 create_vlabel
---------------

(1 row)

-- After creating a new vertex label.
SELECT * FROM ag_catalog.ag_label;
       name       | graph | id | kind |          relation          |        seq_name
------------------+-------+----+------+----------------------------+-------------------------
 _ag_label_vertex | 68484 |  1 | v    | new_graph._ag_label_vertex | _ag_label_vertex_id_seq
 _ag_label_edge   | 68484 |  2 | e    | new_graph._ag_label_edge   | _ag_label_edge_id_seq
 Person           | 68484 |  3 | v    | new_graph.'Person'         | Person_id_seq
(3 rows)

Whenever a vertex label is created with the create_vlabel() function, a new table is generated within the new_graph namespace: new_graph.'label'. The same works for the create_elabel() function for the creation of edge labels. Creating vertices and edges with Cypher will automatically make these tables.

-- Creating two vertices and one edge.
SELECT * FROM cypher('new_graph', $$
CREATE (:Person {name: 'Daedalus'})-[:FATHER_OF]->(:Person {name: 'Icarus'})
$$) AS (a agtype);
 a
---
(0 rows)

-- Showing the newly created tables.
SELECT * FROM ag_catalog.ag_label;
       name       | graph | id | kind |          relation          |        seq_name
------------------+-------+----+------+----------------------------+-------------------------
 _ag_label_vertex | 68484 |  1 | v    | new_graph._ag_label_vertex | _ag_label_vertex_id_seq
 _ag_label_edge   | 68484 |  2 | e    | new_graph._ag_label_edge   | _ag_label_edge_id_seq
 Person           | 68484 |  3 | v    | new_graph.'Person'         | Person_id_seq
 FATHER_OF        | 68484 |  4 | e    | new_graph.'FATHER_OF'      | FATHER_OF_id_seq
(4 rows)

Note

It is recommended that no DML or DDL commands are executed in the namespace that is reserved for the graph.

F.3.3. The apache_age Cypher Query Format

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

cypher(graph_name name, query_string cstring, parameters agtype) returns setof record

Executes the Cypher query passed as an argument. If the Cypher query does not return results, a record definition still needs to be defined. The parameter map specified as parameters can only be used with prepared statements. An error will be thrown otherwise.

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

F.3.3.1. 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.

F.3.3.2. 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.

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.

F.3.4. Data Types — Introduction to agtype

apache_age uses a custom data type called agtype, which is the only data type returned by apache_age. agtype is a superset of json and a custom implementation of jsonb.

F.3.4.1. Simple Data Types

F.3.4.1.1. Null

In Cypher, null is used to represent missing or undefined values. Conceptually, null means a missing unknown value, and it is treated 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.

The following query returns null as an empty space.

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

 null_result
--------------

(1 row)

The concept of NULL in agtype and Postgres Pro is the same as it is in Cypher.

F.3.4.1.2. Integer

The integer type stores whole numbers, i.e. numbers without fractional components. Integer data type is a 64-bit field that stores values from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807. Attempts to store values outside this 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 used only if disk space is at a premium. The bigint type is designed to be used when the range of the integer type is insufficient.

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

 int_result
--------------
1
(1 row)
F.3.4.1.3. 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:

  • If you require exact storage and calculations (such as for monetary amounts), use the numeric type instead.

  • If you want to do complicated calculations with these types for anything important, especially if you rely on certain behavior in boundary cases (infinity, underflow), you should evaluate the implementation carefully.

  • Comparing two floating-point values for equality might not always work as expected.

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:

  • Infinity

  • -Infinity

  • NaN

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

Note that IEEE 754 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, apache_age evaluates 'NaN'::float = 'NaN'::float to true. See Comparability and Equality for more details.

To use a float, denote a decimal value.

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

float_result
--------------
1.0
(1 row)
F.3.4.1.4. 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 types.

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.

Specifying NUMERIC 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 are 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, apache_age evaluates 'NaN'::numeric = 'NaN':numeric to true. See 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.

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

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

numeric_result
--------------
1.0::numeric
(1 row)
F.3.4.1.5. Bool

apache_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.

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

boolean_result
--------------
true
(1 row)

Unlike Postgres Pro, in apache_age boolean outputs as the full word, i.e. true and false as opposed to t and f.

F.3.4.1.6. String

agtype string literals can contain the following escape sequences:

Table F.2. Escape Sequences

Escape SequenceCharacter
\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 \u)

Use single (') quotes to identify a string. The output will use double (") quotes.

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

string_result
--------------
"This is a string"
(1 row)

F.3.4.2. Composite Data Types

F.3.4.2.1. List

All examples will use the WITH clause and RETURN clause.

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

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

                lst
------------------------------------
 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
(1 row)

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.

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

  lst
--------
 [null]
(1 row)

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.

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);

 element
---------
 3
(1 row)

Map elements in lists:

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);

                       map_value
-------------------------------------------------------
 [0, {"key": "key_value"}, 2, 3, 4, 5, 6, 7, 8, 9, 10]
(1 row)

Accessing map elements in lists:

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);

  map_value
-------------
 "key_value"
(1 row)

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

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);

 element
---------
 8
(1 row)

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

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);

  element
-----------
 [0, 1, 2]
(1 row)

Negative index ranges:

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);

        lst
--------------------
 [0, 1, 2, 3, 4, 5]
(1 row)

Positive slices:

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);

     lst
--------------
 [0, 1, 2, 3]
(1 row)

Negative slices:

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);

       lst
------------------
 [6, 7, 8, 9, 10]
(1 row)

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

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);

 element
---------

(1 row)
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);

       element
---------------------
 [5, 6, 7, 8, 9, 10]
(1 row)
F.3.4.2.2. Map

Maps can be constructed using Cypher.

You can construct a simple map with simple agtype values.

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);

                                                  m
------------------------------------------------------------------------------------------------------
 {"int_key": 1, "bool_key": true, "float_key": 1.0, "string_key": "Value", "numeric_key": 1::numeric}
(1 row)

A map can also contain composite data types, i.e. lists and other maps.

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

                                    m
-------------------------------------------------------------------------
 {"mapKey": {"i": 0}, "listKey": [{"inner": "Map1"}, {"inner": "Map2"}]}
(1 row)

Property access of a map:

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);

 int_key
---------
 1
(1 row)

Accessing list elements in maps:

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

         m
-------------------
 {"inner": "Map1"}
(1 row)

F.3.4.3. 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.

F.3.4.3.1. Graph ID

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

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

  • Edges are required to have a label, but vertices do not.

  • The names of labels between vertices and edges cannot overlap.

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

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

F.3.4.4. Vertex

  • A vertex is the basic entity of the graph, with the unique attribute of being able to exist in and of itself.

  • A vertex may be assigned a label.

  • A vertex may have zero or more outgoing edges.

  • A vertex may have zero or more incoming edges.

Table F.3. Data Format

Attribute NameDescription
id Graph ID 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:

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

                                v
------------------------------------------------------------------
 {"id": 0, "label": "label_name", "properties": {"i": 0}}::vertex
(1 row)

F.3.4.5. Edge

An edge is an entity that encodes a directed connection between exactly two nodes, the source 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.

Table F.4. Data Format

Attribute NameDescription
id Graph ID for this edge
startid Graph ID for the source node
endid Graph ID for the target node
label Name of the label this vertex has
properties Properties associated with this vertex

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

Type casting a map to an edge:

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);

                                             e
--------------------------------------------------------------------------------------------
 {"id": 2, "label": "label_name", "end_id": 1, "start_id": 0, "properties": {"i": 0}}::edge
(1 row)

F.3.4.6. Composite Entities

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 list to a path:

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);

                                                                                                                  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)

F.3.5. Comparability, Equality, Orderability and Equivalence

apache_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 Pro defined logic and the openCypher specification:

  • Comparability between values of different types is defined. This deviation is particularly pronounced when it occurs as part of the evaluation of predicates (in WHERE).

  • ORDER BY will not fail if the values passed to it have different types.

The underlying conceptual model is complex and sometimes inconsistent. This leads to an unclear relationship between comparison operators, equality, grouping, and ORDER BY. Comparability and orderability are aligned with each other consistently, as all types can be ordered and compared. The difference between equality and equivalence, as exposed by IN, =, DISTINCT, and grouping, in apache_age is limited to testing two instances of the value null to each other. In equality, null = null is null. In equivalence, used by DISTINCT and when grouping values, two null values are always treated as being the same value. However, equality treats null values differently if they are an element of a list or a map value.

F.3.5.1. Concepts

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

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

  • 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 is used by the ORDER BY clause, and defines the underlying semantics of how to order values.

  • 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.

F.3.5.2. 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 Postgres Pro, for the same query. So we prioritize equivalence and orderability over equality and comparability to allow for ordering of output data.

F.3.5.2.1. Comparability

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

  • Numbers

    • Numbers of different types (excluding NaN values and the Infinities) are compared to each other as if both numbers would have been coerced to arbitrary precision bigdecimal (currently outside the Cypher type system) before comparing them with each other numerically in ascending order.

    • Comparison to any value that is not also number follows the rules of orderability.

    • Floats do not have the required precision to represent all of the whole numbers in the range of agtype integer and agtype numeric. When casting an integer or agtype numeric to a float, unexpected results can occur when casting values in the high and low range.

    • Integers

      • Integers are compared numerically in ascending order.

    • Floats

      • Floats (excluding NaN values and the Infinities) are compared numerically in ascending order.

      • Positive infinity is of type FLOAT, equal to itself and greater than any other number, except NaN values.

      • Negative infinity is of type FLOAT, equal to itself and less than any other number.

      • NaN values are comparable to each and greater than any other float value.

    • Numeric

      • Numerics are compared numerically in ascending order.

  • Booleans

    • Booleans are compared such that false is less than true.

    • Comparison to any value that is not also a boolean follows the rules of orderability.

  • Strings

    • Strings are compared in dictionary order, i.e. characters are compared pairwise in ascending order from the start of the string to the end. Characters missing in a shorter string are considered to be less than any other character. For example, 'a' < 'aa'.

    • Comparison to any value that is not also a string follows the rules of orderability.

  • Lists

    • Lists are compared in sequential order, i.e. list elements are compared pairwise in ascending order from the start of the list to the end. Elements missing in a shorter list are considered to be less than any other value (including null values), for example, [1] < [1, 0] but also [1] < [1, null].

    • Comparison to any value that is not also a list follows the rules of orderability.

  • Maps

    • The comparison order for maps is unspecified and left to implementations.

    • The comparison order for maps must align with the equality semantics outlined below. In consequence, any map that contains an entry that maps its key to a null value is incomparable. For example, {a: 1} <= {a: 1, b: null} evaluates to null.

    • Comparison to any value that is not also a regular map follows the rules of orderability.

Entities:

  • Vertices: The comparison order for vertices is based on the assigned graphid.

  • Edges: The comparison order for edges is based on the assigned graphid.

  • Paths: Paths are compared as if they were a list of alternating nodes and relationships of the path from the start node to the end node. For example, given nodes n1, n2, n3, and relationships r1 and r2, and given that n1 < n2 < n3 and r1 < r2, then the path p1 from n1 to n3 via r1 would be less than the path p2 to n1 from n2 via r2. Paths are expressed in terms of lists:

    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
    

    Note

    Comparison to any value that is not also a path will return false.

  • NULL: null is incomparable with any other value (including other null values.)

F.3.5.3. Orderability Between Different agtype Types

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

  1. Path

  2. Edge

  3. Vertex

  4. Object

  5. Array

  6. String

  7. Bool

  8. Numeric, Integer, Float

  9. NULL

Note

This is subject to change in future releases.

F.3.6. Operators

F.3.6.1. String Specific Comparison Operators

SELECT * FROM cypher('graph_name', $$
CREATE (:Person {name: 'John'}),
       (:Person {name: 'Jeff'}),
       (:Person {name: 'Joan'}),
       (:Person {name: 'Bill'})
$$) AS (result agtype);
Starts With

Performs case-sensitive prefix searching on strings.

SELECT * FROM cypher('graph_name', $$
	MATCH (v:Person)
	WHERE v.name STARTS WITH "J"
	RETURN v.name
$$) AS (names agtype);

 names
--------
 "John"
 "Jeff"
 "Joan"
(3 rows)
Contains

Performs case-sensitive inclusion searching in strings.

SELECT * FROM cypher('graph_name', $$
	MATCH (v:Person)
	WHERE v.name CONTAINS "o"
	RETURN v.name
$$) AS (names agtype);

 names
--------
 "John"
 "Joan"
(2 rows)
Ends With

Performs case-sensitive suffix searching on strings.

SELECT * FROM cypher('graph_name', $$
	MATCH (v:Person)
	WHERE v.name ENDS WITH "n"
	RETURN v.name
$$) AS (names agtype);

 names
--------
 "John"
 "Joan"
(2 rows)
F.3.6.1.1. Regular Expressions

apache_age supports the use of POSIX regular expressions using the =~ operator. By default =~ is case sensitve.

Basic String Matching

The =~ operator when no special characters are given, act like the = operator.

SELECT * FROM cypher('graph_name', $$
	MATCH (v:Person)
	WHERE v.name =~ 'John'
	RETURN v.name
$$) AS (names agtype);

 names
--------
 "John"
(1 row)

Adding (?i) at the beginning of the string will make the comparison case insensitive.

SELECT * FROM cypher('graph_name', $$
	MATCH (v:Person)
	WHERE v.name =~ '(?i)JoHn'
	RETURN v.name
$$) AS (names agtype);

 names
--------
 "John"
(1 row)
The . Wildcard

The . operator acts as a wildcard to match any single character.

SELECT * FROM cypher('graph_name', $$
	MATCH (v:Person)
	WHERE v.name =~ 'Jo.n'
	RETURN v.name
$$) AS (names agtype);

 names
--------
 "John"
 "Joan"
(2 rows)
The * Wildcard

The * wildcard after a character will match to 0 or more of the previous character.

SELECT * FROM cypher('graph_name', $$
	MATCH (v:Person)
	WHERE v.name =~ 'Johz*n'
	RETURN v.name
$$) AS (names agtype);

names
--------
 "John"
(1 row)
The + Operator

The + operator matches to 1 or more the previous character.

SELECT * FROM cypher('graph_name', $$
	MATCH (v:Person)
	WHERE v.name =~ 'Bil+'
	RETURN v.name
$$) AS (names agtype);

 names
--------
 "Bill"
(1 row)
The . and * Wildcards Together

You can use the . and * wildcards together to represent the rest of the string.

SELECT * FROM cypher('graph_name', $$
	MATCH (v:Person)
	WHERE v.name =~ 'J.*'
	RETURN v.name
$$) AS (names agtype);

 names
--------
 "John"
 "Jeff"
 "Joan"
(3 rows)

F.3.6.2. Operator Precedence

Operator precedence in apache_age is shown below:

Table F.5. Operator Precedence

PrecedenceOperatorDescription
1.Property Access
2[]Map and List Subscripting
 ()Function Call
3STARTS WITHCase-sensitive prefix searching on strings
 ENDS WITHCase-sensitive suffix searching on strings
 CONTAINSCase-sensitive inclusion searching on strings
 =~Regular expression string matching
4-Unary Minus
5INChecking if an element exists in a list
 IS NULLChecking a value is NULL
 IS NOT NULLChecking 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
10NOTLogical NOT
11ANDLogical AND
12ORLogical OR

F.3.7. Aggregation

Generally an aggregation aggr(expr) processes all matching rows for each aggregation key found in an incoming record (keys are compared using equivalence).

In a regular aggregation (i.e. of the form aggr(expr)), the list of aggregated values is the list of candidate values with all null values removed from it.

SELECT * FROM cypher('graph_name', $$
    CREATE (a:Person {name: 'A', age: 13}),
    (b:Person {name: 'B', age: 33, eyes: "blue"}),
    (c:Person {name: 'C', age: 44, eyes: "blue"}),
    (d1:Person {name: 'D', eyes: "brown"}),
    (d2:Person {name: 'D'}),
    (a)-[:KNOWS]->(b),
    (a)-[:KNOWS]->(c),
    (a)-[:KNOWS]->(d1),
    (b)-[:KNOWS]->(d2),
    (c)-[:KNOWS]->(d2)
$$) as (a agtype);

F.3.7.1. Auto Group By

To calculate aggregated data, Cypher offers aggregation, analogous to SQL GROUP BY.

Aggregating functions take a set of values and calculate an aggregated value over them. Examples are avg() that calculates the average of multiple numeric values, or min() that finds the smallest numeric or string value in a set of values. When we say below that an aggregating function operates on a set of values, we mean these to be the result of the application of the inner expression (such as n.age) to all the records within the same aggregation group.

Aggregation can be computed over all the matching subgraphs, or it can be further divided by introducing grouping keys. These are non-aggregate expressions, that are used to group the values going into the aggregate functions.

Assume we have the following RETURN statement:

SELECT * FROM cypher('graph_name', $$
    MATCH (v:Person)
    RETURN v.name, count(*)
$$) as (grouping_key agtype, count agtype);

 grouping_key | count
--------------+-------
 "A"          | 1
 "D"          | 2
 "B"          | 1
 "C"          | 1
(4 rows)

We have two return expressions: grouping_key, and count(*). The first, grouping_key, is not an aggregate function, and so it will be the grouping key. The latter, count(*) is an aggregate expression. The matching subgraphs will be divided into different buckets, depending on the grouping key. The aggregate function will then be run on these buckets, calculating an aggregate value per bucket.

F.3.7.2. Sorting on Aggregate Functions

To use aggregations to sort the result set, the aggregation must be included in the RETURN to be used in the ORDER BY.

SELECT *
FROM cypher('graph_name', $$
    MATCH (me:Person)-[]->(friend:Person)
    RETURN count(friend), me
    ORDER BY count(friend)
$$) as (friends agtype, me agtype);

F.3.7.3. Distinct aggregation

In a distinct aggregation (i.e. of the form aggr(DISTINCT expr)), the list of aggregated values is the list of candidate values with all null values removed from it. Furthermore, in a distinct aggregation, only one of all equivalent candidate values is included in the list of aggregated values, i.e. duplicates under equivalence are removed.

The DISTINCT operator works in conjunction with aggregation. It is used to make all values unique before running them through an aggregate function.

SELECT *
FROM cypher('graph_name', $$
    MATCH (v:Person)
    RETURN count(DISTINCT v.eyes), count(v.eyes)
$$) as (distinct_eyes agtype, eyes agtype);

 distinct_eyes | eyes
---------------+------
 2             | 3
(1 row)

F.3.7.4. Ambiguous Grouping Statements

This feature of not requiring the user to specify their grouping keys for a query allows for ambiguity on what Cypher should qualify as their grouping keys.

SELECT * FROM cypher('graph_name', $$
CREATE (:L {a: 1, b: 2, c: 3}),
       (:L {a: 2, b: 3, c: 1}),
       (:L {a: 3, b: 1, c: 2})
$$) as (a agtype);
F.3.7.4.1. Invalid Query in apache_age

apache_age solution to this problem is to not allow a WITH or RETURN column to combine aggregate functions with variables that are not explicitly listed in another column of the same WITH or RETURN clause.

SELECT * FROM cypher('graph_name', $$
    MATCH (x:L)
    RETURN x.a + count(*) + x.b + count(*) + x.c
$$) as (a agtype);

ERROR:  'x' must be either part of an explicitly listed key or used inside an aggregate function
LINE 3: RETURN x.a + count(*) + x.b + count(*) + x.c
F.3.7.4.2. Valid Query in apache_age

Columns that do not include an aggregate function in apache_age are considered to be the grouping keys for that WITH or RETURN clause.

For the above query, the user could rewrite the query in several ways that will return results.

SELECT * FROM cypher('graph_name', $$
    MATCH (x:L)
    RETURN (x.a + x.b + x.c) + count(*) + count(*), x.a + x.b + x.c
$$) as (count agtype, key agtype);

count  | key
-------+-----
 12    | 6
(1 row)

x.a + x.b + x.c is the grouping key. Grouping keys created like this must include parenthesis.

SELECT * FROM cypher('graph_name', $$
    MATCH (x:L)
    RETURN x.a + count(*) + x.b + count(*) + x.c, x.a, x.b, x.c
$$) as (count agtype, a agtype, b agtype, c agtype);

 count | a | b | c
-------+---+---+---
 10    | 3 | 1 | 2
 10    | 2 | 3 | 1
 10    | 1 | 2 | 3
(3 rows)

x.a, x.b, and x.c will be considered different grouping keys.

F.3.7.4.3. Vertices and Edges in Ambiguous Grouping

Alternatively, the grouping key can be a vertex or edge, and then any properties of the vertex or edge can be specified without being explicitly stated in a WITH or RETURN column.

SELECT * FROM cypher('graph_name', $$
    MATCH (x:L)
    RETURN count(*) + count(*) + x.a + x.b + x.c, x
$$) as (count agtype, key agtype);

 count |                                          key
-------+----------------------------------------------------------------------------------------
 8     | {"id": 1407374883553283, "label": "L", "properties": {"a": 3, "b": 1, "c": 2}}::vertex
 8     | {"id": 1407374883553281, "label": "L", "properties": {"a": 1, "b": 2, "c": 3}}::vertex
 8     | {"id": 1407374883553282, "label": "L", "properties": {"a": 2, "b": 3, "c": 1}}::vertex
(3 rows)

Results will be grouped on x, because it is safe to assume that properties considered unnecessary for grouping are unambiguous.

F.3.7.4.4. Hiding Unwanted Grouping Keys

If the grouping key is considered unnecessary for the query output, the aggregation can be done in a WITH clause then passing information to the RETURN clause.

SELECT * FROM cypher('graph_name', $$
    MATCH (x:L)
    WITH count(*) + count(*) + x.a + x.b + x.c as column, x
    RETURN column
$$) as (a agtype);

 a
---
 8
 8
 8
(3 rows)

F.3.8. Importing Graph from Files

You can use the following instructions to create a graph from the files:

  • Functions to load graphs from files

  • Structure of CSV files that load functions as input

  • Simple source code example to load countries and cities from the files

Note

User must create graph and labels before loading data from files.

F.3.8.1. Load Graph Functions

Following are the details about the functions to create vertices and edges from the file.

Function load_labels_from_file is used to load vertices from CSV files.

load_labels_from_file('graph_name',
                      'label_name',
                      'file_path')

By adding the fourth parameter user can exclude the id field. Use this when there is no id field in the file.

load_labels_from_file('graph_name',
                      'label_name',
                      'file_path',
                      false)

Function load_edges_from_file can be used to load edges from the CSV file. See the file structure below.

Note

Make sure that IDs in the edge file are identical to ones that are in vertices files.

load_edges_from_file('graph_name',
                    'label_name',
                    'file_path');

F.3.8.2. Explanation about the CSV format

Following is the explanation about the structure for CSV files for vertices and edges.

A CSV file for nodes is formatted as follows:

Table F.6. CSV File Format for Nodes

Field name Field description
id The first column of the file. All values are a positive integer. This is an optional field when id_field_exists is false. However, it should be present when id_field_exists is not set to false.
Properties All other columns contain the properties for the nodes. Header row contains the name of property.

Similarly, a CSV file for edges is formatted as follows:

Table F.7. CSV File Format for Edges

Field name Field description
start_id Node ID of the node from where the edge is started. This ID is present in nodes.csv file.
start_vertex_type Class of the node
end_id End ID of the node at which the edge is terminated.
end_vertex_type Class of the node
properties Properties of the edge. The header contains the property name.

F.3.8.3. Example SQL Script

  • Load apache_age and create a graph.

    LOAD 'age';
    
    SET search_path TO ag_catalog;
    SELECT create_graph('agload_test_graph');
    
  • Create label Country and load vertices from the CSV file. Note that this CSV file has the id field.

    SELECT create_vlabel('agload_test_graph','Country');
    SELECT load_labels_from_file('agload_test_graph',
                                 'Country',
                                 '/age/regress/age_load/data/countries.csv');
    
  • Create label City and load vertices from the CSV file. Note that this CSV file has the id field.

    SELECT create_vlabel('agload_test_graph','City');
    SELECT load_labels_from_file('agload_test_graph',
                                 'City',
                                 '/age/regress/age_load/data/cities.csv');
    
  • Create label has_city and load edges from the CSV file.

    SELECT create_elabel('agload_test_graph','has_city');
    SELECT load_edges_from_file('agload_test_graph', 'has_city',
         '/age/regress/age_load/data/edges.csv');
    
  • Check if the graph has been loaded properly.

    SELECT table_catalog, table_schema, table_name, table_type
    FROM information_schema.tables
    WHERE table_schema = 'agload_test_graph';
    
    SELECT COUNT(*) FROM agload_test_graph."Country";
    SELECT COUNT(*) FROM agload_test_graph."City";
    SELECT COUNT(*) FROM agload_test_graph."has_city";
    
    SELECT COUNT(*) FROM cypher('agload_test_graph', $$MATCH(n) RETURN n$$) as (n agtype);
    SELECT COUNT(*) FROM cypher('agload_test_graph', $$MATCH (a)-[e]->(b) RETURN e$$) as (n agtype);
    
F.3.8.3.1. Creating Vertices Without ID Field in the File
  • Create label Country2 and load vertices from the CSV file. Note that this CSV file has no id field.

    SELECT create_vlabel('agload_test_graph','Country2');
    SELECT load_labels_from_file('agload_test_graph',
                                 'Country2',
                                 '/age/regress/age_load/data/countries.csv',
                                 false);
    
  • Create label City2 and load vertices from CSV file. Note this CSV file has no id field.

    SELECT create_vlabel('agload_test_graph','City2');
    SELECT load_labels_from_file('agload_test_graph',
                                 'City2',
                                 '/age/regress/age_load/data/cities.csv',
                                 false);
    
  • Check if the graph has been loaded properly and perform difference analysis between IDs created automatically and picked from the files.

    Labels Country and City were created with the id field in the file.

    Labels Country2 and City2 were created with no id field in the file.

    SELECT COUNT(*) FROM agload_test_graph."Country2";
    SELECT COUNT(*) FROM agload_test_graph."City2";
    
    SELECT id FROM agload_test_graph."Country" LIMIT 10;
    SELECT id FROM agload_test_graph."Country2" LIMIT 10;
    
    SELECT * FROM cypher('agload_test_graph', $$MATCH(n:Country {iso2 : 'BE'})
        RETURN id(n), n.name, n.iso2 $$) as ('id(n)' agtype, 'n.name' agtype, 'n.iso2' agtype);
    SELECT * FROM cypher('agload_test_graph', $$MATCH(n:Country2 {iso2 : 'BE'})
        RETURN id(n), n.name, n.iso2 $$) as ('id(n)' agtype, 'n.name' agtype, 'n.iso2' agtype);
    
    SELECT * FROM cypher('agload_test_graph', $$MATCH(n:Country {iso2 : 'AT'})
        RETURN id(n), n.name, n.iso2 $$) as ('id(n)' agtype, 'n.name' agtype, 'n.iso2' agtype);
    SELECT * FROM cypher('agload_test_graph', $$MATCH(n:Country2 {iso2 : 'AT'})
        RETURN id(n), n.name, n.iso2 $$) as ('id(n)' agtype, 'n.name' agtype, 'n.iso2' agtype);
    
    SELECT drop_graph('agload_test_graph', true);
    

F.3.9. MATCH

The MATCH clause allows you to specify the patterns a query will search for in the database. This is the primary way of retrieving data for use in a query.

A WHERE clause often follows a MATCH clause to add user-defined restrictions to the matched patterns to manipulate the set of data returned. 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 clause. 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 a declarative language, and so typically 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 the match is found.

F.3.9.1. Basic Vertex Finding

F.3.9.1.1. Get All Vertices

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

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

                                      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 rows)

Returns all vertices in the database.

F.3.9.1.2. Get All Vertices with a Label

Getting all vertices with a label is done with a single node pattern where the vertex has the label specified as follows:

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

         title
------------------------
'Wall Street'
'The American President'
(2 rows)

Returns all the movies in the database.

F.3.9.1.4. Match with Labels

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

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

    title
-------------
'Wall Street'
(1 row)

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

F.3.9.2. Edge Basics

F.3.9.2.1. Outgoing Edges

To return directed edges, you may use -> or <- to specify the direction of which the edge points.

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

    title
-------------
'Wall Street'
(1 row)

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

F.3.9.2.2. Directed Edges and Variable

If a variable is required, either for filtering on properties of the edge, or to return the edge, specify the variable within the edge or vertex you wish to use.

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

  title
----------
'DIRECTED'
(1 row)

Returns the type of each outgoing edge from Oliver.

F.3.9.2.3. Match on Edge Label

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

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

   actors_name
-----------------
'Charlie Sheen'
'Martin Sheen'
'Michael Douglas'
(3 rows)

Returns all actors that ACTED_IN Wall Street.

F.3.9.2.4. Match on Edge Label with a Variable

If you want to use a variable to hold the edge, and specify the edge label you want, you can do so by specifying them both.

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

     role
--------------
'Gordon Gekko'
'Carl Fox'
'Bud Fox'
(3 rows)

Returns ACTED_IN roles for Wall Street.

F.3.9.2.5. Multiple Edges

Edges can be strung together to match an infinite number of edges. As long as the base pattern ()-[]-() is followed, users can chain together edges and vertices to match specific patterns.

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);

    title	    |     name
--------------+--------------
'Wall Street' |	'Oliver Stone'
(1 row)

Returns the movie Charlie Sheen acted in and its director.

F.3.9.3. Variable Length Edges

When the connection between two vertices is of variable length, the list of edges that form the connection can be returned using the following connection.

Rather than describing a long path using a sequence of many vertex and edge descriptions in a pattern, many edges (and the intermediate vertices) can be described by specifying a length in the edge description of a pattern.

(u)-[*2]->(v)

Which describes a right directed path of three vertices and two edges can be rewritten to:

(u)-[]->()-[]->(v)

A range length can also be given:

(u)-[*3..5]->(v)

Which is equivalent to:

(u)-[]->()-[]->()-[]->(v) and
(u)-[]->()-[]->()-[]->()-[]->(v) and
(u)-[]->()-[]->()-[]->()-[]->()-[]->(v)

The previous example provided gave the path both a lower and upper bound for the number of edges (and vertices) between u and v. Either one or both of these binding values can be excluded.

(u)-[*3..]->(v)

Returns all paths between u and v that have three or more edges included.

(u)-[*..5]->(v)

Returns all paths between u and v that have 5 or fewer edges included.

(u)-[*]->(v)

Returns all paths between u and v.

F.3.9.3.1. Example
SELECT * FROM cypher('graph_name', $$
    MATCH p = (actor {name: 'Willam Dafoe'})-[:ACTED_IN*2]-(co_actor)
    RETURN relationships(p)
$$) as (r agtype);

                                                                               r
------------------------------------------------------------------------------------------------------------------------------------------------------------------
[{id: 0; label:"ACTED_IN"; properties: {role: "Green Goblin"}}::edge, {id: 1; label: "ACTED_IN; properties: {role: "Spiderman", actor: "Toby Maguire}}::edge]
[{id: 0; label:"ACTED_IN"; properties: {role: "Green Goblin"}}::edge, {id: 2; label: "ACTED_IN; properties: {role: "Spiderman", actor: "Andrew Garfield"}}::edge]
(2 rows)

Returns the list of edges, including the one that Willam Dafoe acted in and the two Spiderman actors he worked with.

F.3.10. WITH

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, an optional WITH clause can be used to do so.

F.3.10.1. Filter on Aggregate Function Results

Aggregated results have to pass through a WITH clause to be able to filter on.

SELECT *
FROM cypher('graph_name', $$
    MATCH (david {name: 'David'})-[]-(otherPerson)-[]->()
    WITH otherPerson, count(*) AS foaf
    WHERE foaf > 1
    RETURN otherPerson.name
$$) as (name agtype);

  name
--------
"Anders"
(1 row)

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

F.3.10.2. Sort Results Before Using collect

You can sort results before passing them to collect, thus sorting the resulting list.

SELECT *
FROM cypher('graph_name', $$
    MATCH (n)WITH n
    ORDER BY n.name DESC LIMIT 3
    RETURN collect(n.name)
$$) as (names agtype);

         names
-------------------------
["Emil","David","Ceasar"]
(1 row)

A list of the names of people in reverse order, limited to 3, is returned.

F.3.11. RETURN

In the RETURN part of your query, you define which parts of the pattern you want to output. Output can include agtype values, nodes, relationships, or properties.

F.3.11.1. Return Nodes

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

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

                       n
---------------------------------------------------
{id: 0; label: '' properties: {name: 'B'}}::vertex
(1 row)

The example will return the node.

F.3.11.2. Return Edges

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

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

                              r
-------------------------------------------------------------------
 {id: 2; startid: 0; endid: 1; label: 'KNOWS' properties: {}}::edge
(1 row)

The relationship is returned by the example.

F.3.11.3. Return Property

To return a property, use the dot separator, as follows:

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

 name
------
 'A'
(1 row)

The value of the property name gets returned.

F.3.11.4. Return All Elements

When you want to return all vertices, edges and paths found in a query, you can use the * symbol.

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

                                             a                                                          |                                                                 b	                                                          |                                 r
--------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------
 {"id": 281474976710659, "label": "", "properties": {"age": 55, "name": "A", "happy": "Yes!"}}::vertex	| {"id": 1125899906842625, "label": "BLOCKS", "end_id": 281474976710660, "start_id": 281474976710659, "properties": {}}::edge	| {"id": 281474976710660, "label": "", "properties": {"name": "B"}}::vertex
 {"id": 281474976710659, "label": "", "properties": {"age": 55, "name": "A", "happy": "Yes!"}}::vertex	| {"id": 1407374883553281, "label": "KNOWS", "end_id": 281474976710660, "start_id": 281474976710659, "properties": {}}::edge	| {"id": 281474976710660, "label": "", "properties": {"name": "B"}}::vertex
(2 rows)

This returns the two vertices, and the edge used in the query.

F.3.11.5. 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:

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);

 happy
-------
 "Yes!"
(1 row)

The node with name A is returned.

F.3.11.6. 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.

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

 objects_name
-------------
 'A'
(1 row)

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

F.3.11.7. Optional Properties

If a property might or might not be there, it will be treated as null if it is missing.

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

 age
-----
 55
 NULL
(2 rows)

This query returns the property if it exists, or null if the property does not exist.

F.3.11.8. Other Expressions

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

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);

 older_than_30 | literal         | id
---------------+-----------------+----
 true          | 'I'm a literal' | 1
(1 row)

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

F.3.11.9. Unique Results

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

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

                          b
----------------------------------------------------
 {id: 1; label: '' properties: {name: 'B'}}::vertex
(1 row)

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

F.3.12. ORDER BY

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

Note that you cannot sort on nodes or relationships, sorting must be done on properties. 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.

F.3.12.1. Order Nodes by Property

ORDER BY is used to sort the output.

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);

  name  | age
--------+-----
  "A"	  | 34
  "B"	  | 34
  "C"	  | 32
(3 rows)

The nodes are returned, sorted by their name.

F.3.12.2. 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.

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);

  name  | age
--------+-----
  "C"	  | 32
  "A"	  | 34
  "B"	  | 34
(3 rows)

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

F.3.12.3. Order Nodes in Descending Order

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

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);

  name  | age
--------+-----
  "C"	  | 32
  "B"	  | 34
  "A"	  | 34
(3 rows)

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

F.3.12.4. Ordering null

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

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

  name  | age | height
--------+-----+--------
  "A"	  | 34  | 170
  "C"	  | 32  | 185
  "B"	  | 34  | NULL
(3 rows)

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

F.3.13. SKIP

SKIP defines from which record to start including the records in the output.

By using SKIP, the result set will get trimmed from the top. Please note that no guarantees are made on the order of the returned results unless specified by the ORDER BY clause. SKIP accepts any expression that evaluates to a positive integer.

F.3.13.1. Skip First Three Rows

To return a subset of the result, starting from the top, use the following syntax:

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

 names
-------
 "D"
 "E"
(2 rows)

The name property of the matched node n is returned, with a limit of 3.

F.3.13.2. Return Middle Two Rows

To return a subset of the result, starting in the middle, use this syntax:

SELECT *
FROM cypher('graph_name', $$
    MATCH (n)
    RETURN n.name
    ORDER BY n.name
    SKIP 1
    LIMIT 2
$$) as (names agtype);

 names
-------
 "B"
 "C"
(2 rows)

Two vertices from the middle are returned.

F.3.13.3. Using an Expression with SKIP to Return a Subset of Rows

Using an expression with SKIP to return a subset of the rows.

SELECT *
FROM cypher('graph_name', $$
    MATCH (n)
    RETURN n.name
    ORDER BY n.name
    SKIP (3 * rand())+ 1
$$) as (a agtype);

 names
-------
 "C"
 "D"
 "E"
(3 rows)

The first two vertices are skipped, and only the last three are returned in the result.

F.3.14. LIMIT

LIMIT constrains the number of records in the output.

LIMIT accepts any expression that evaluates to a positive integer.

F.3.14.1. Return a Subset of the Rows

To return a subset of the result, starting from the top, use this syntax:

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

names
"A"
"B"
"C"
3 rows

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

F.3.14.2. Using an Expression with LIMIT to Return a Subset of Rows

LIMIT accepts any expression that evaluates to a positive integer as long as it is not referring to any external variables:

SELECT *
FROM cypher('graph_name', $$
    MATCH (n)
    RETURN n.name
    ORDER BY n.name
    LIMIT toInteger(3 * rand()) + 1
$$) as (names agtype);

 names
-------
 "A"
 "B"
(2 rows)

Returns one to three top items.

F.3.15. CREATE

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

F.3.15.1. Terminal CREATE Clauses

A CREATE clause that is not followed by another clause is a terminal clause. When the 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 the Cypher query ends with a terminal node, define a single value in the column list definition: no data will be returned in this variable.

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

 a
---
(0 rows)

F.3.15.2. Create Single Vertex

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

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

 v
---
(0 rows)

Nothing is returned from this query.

F.3.15.3. Create Multiple Vertices

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

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

   a
-------
(0 rows)

F.3.15.4. Create a Vertex with a Label

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

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

   v
-------
(0 rows)

Nothing is returned from this query.

F.3.15.5. Create Vertex and Add Labels and Properties

You can create a vertex with labels and properties at the same time.

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

   n
-------
(0 rows)

Nothing is returned from this query.

F.3.15.6. Return Created Node

You can create and return a node within the same query as follows:

SELECT *
FROM cypher('graph_name', $$
    CREATE (a {name: 'Andres'})
    RETURN a
$$) as (a agtype);

                                       a
--------------------------------------------------------------------------------
 {"id": 281474976710660, "label": "", "properties": {"name": "Andres"}}::vertex
(1 row)

The newly-created node is returned.

F.3.15.7. Create an Edge Between Two Nodes

To create an edge between two vertices, we first MATCH the two vertices. Once the nodes are matched, we create an edge between them.

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);

                              e
-----------------------------------------------------------------------
{id: 3; startid: 0, endid: 1; label: 'RELTYPE'; properties: {}}::edge
(1 row)

The created edge is returned by the query.

F.3.15.8. Create an Edge and Set Properties

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

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);

                                          e
---------------------------------------------------------------------------------------------------
 {id: 3; startid: 0, endid: 1; label: 'RELTYPE'; properties: {name: 'Node A<->Node B'}}::edge
(1 row)

The newly created edge is returned by the example query.

F.3.15.9. Create a Full Path

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

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

                     p
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 [{"id": 281474976710661, "label": "", "properties": {"name": "Andres"}}::vertex, {"id": 1407374883553282, "label": "WORKS_AT", "end_id": 281474976710662, "start_id": 281474976710661, "properties": {}}::edge, {"id": 281474976710662, "label": "", "properties": {}}::vertex, {"id": 1407374883553281, "label": "WORKS_AT", "end_id": 281474976710662, "start_id": 281474976710663, "properties": {}}::edge, {"id": 281474976710663, "label": "", "properties": {"name": "Michael"}}::vertex]::path
(1 row)

This query creates three nodes and two relationships simultaneously, assigns the pattern to a path variable, and returns the said pattern.

F.3.16. DELETE

The DELETE clause is used to delete graph elements — nodes, relationships, or paths.

A DELETE clause that is not followed by another clause is called a terminal clause. When the 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 the Cypher query ends with a terminal node, define a single value in the column list definition: no data will be returned in this variable.

For removing properties, see Section F.3.17.4.

You cannot delete a node without also deleting edges that start or end on said vertex. Either explicitly delete the vertices,or use DETACH DELETE.

F.3.16.1. Delete Isolated Vertices

To delete a vertex, use the DELETE clause.

SELECT *
FROM cypher('graph_name', $$
    MATCH (v:Useless)
    DELETE v
$$) as (v agtype);

   v
-------
(0 rows)

This will delete the vertices (with label Useless) that have no edges. Nothing is returned from this query.

F.3.16.2. Delete All Vertices and Edges Associated with Them

Running a MATCH clause will collect all nodes — use the DETACH option to delete vertice edges first, and then delete the vertex itself.

SELECT *
FROM cypher('graph_name', $$
    MATCH (v:Useless)
    DETACH DELETE v
$$) as (v agtype);

   v
-------
(0 rows)

Nothing is returned from this query.

F.3.16.3. Delete Edges Only

To delete an edge, use the MATCH clause to find your edges, then add the variable to the DELETE clause.

SELECT *
FROM cypher('graph_name', $$
    MATCH (n {name: 'Andres'})-[r:KNOWS]->()
    DELETE r
$$) as (v agtype);

   v
-------
(0 rows)

Nothing is returned from this query.

F.3.16.4. Return a Deleted Vertex

You can return vertices that have been deleted with a RETURN clause.

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

                                  a
---------------------------------------------------------------------------
 {"id": 281474976710659, "label": "", "properties": {"name": "A"}}::vertex
(1 rows)

F.3.17. SET

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

F.3.17.1. Terminal SET Clauses

A SET clause that is not followed by another clause is a terminal clause. When the 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 the Cypher query ends with a terminal node, define a single value in the column list definition: no data will be returned in this variable.

F.3.17.2. Set a Property

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

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

   v
-------
(0 rows)

The newly changed node is returned by the query.

F.3.17.3. Return Created Vertex

Creating a single vertex is done with the following query:

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

                                                  v
-----------------------------------------------------------------------------------------------------
 {id: 3; label: 'Person'; properties: {surname:"Taylor", name:"Andres", age:36, hungry:true}}::vertex
(1 row)

The newly changed vertex is returned by the query.

F.3.17.4. Remove a Property

Normally a property can be removed by using REMOVE, but users can also remove properties using the SET command. One example is if the property comes from a parameter.

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

                                         v
---------------------------------------------------------------------------------------
 {id: 3; label: 'Person'; properties: {surname:"Taylor", age:36, hungry:true}}::vertex
(1 row)

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

F.3.17.5. Set Multiple Properties Using One SET Clause

If you want to set multiple properties in one query, you can separate them with a comma.

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

                                                    v
------------------------------------------------------------------------------------------------------------------------------
{"id": 281474976710661, "label": "", "properties": {"name": "Andres", "surname": "Taylor", "position": "Developer"}}: :vertex
(1 row)

F.3.18. REMOVE

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

A REMOVE clause that is not followed by another clause is called a terminal clause. When the 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 the Cypher query ends with a terminal node, define a single value in the column list definition: no data will be returned in this variable.

Cypher does not allow storing null in properties. Instead, if no value exists, the property is just not there. So, removing a property value on a node or a relationship is also done with REMOVE.

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

                          andres
---------------------------------------------------------------
 {id: 3; label: 'Person'; properties: {name:"Andres"}}::vertex
(1 row)

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

F.3.19. MERGE

The MERGE clause ensures that a pattern exists in the graph. Either the pattern already exists, or it needs to be created.

MERGE either matches existing nodes, or creates new data. It is a combination of MATCH and CREATE.

For example, you can specify that the graph must contain a node for a user with a certain name. If there is not a node with the correct name, a new node will be created and its name property set. When using MERGE on full patterns, the behavior is that either the whole pattern matches, or the whole pattern is created. MERGE will not partially use existing patterns. If partial matches are needed, this can be accomplished by splitting a pattern up into multiple MERGE clauses.

As with MATCH, MERGE can match multiple occurrences of a pattern. If there are multiple matches, they will all be passed on to later stages of the query.

SELECT * from cypher('graph_name', $$
CREATE (A:Person {name: 'Charlie Sheen', bornIn: 'New York'}),
    (B:Person {name: 'Michael Douglas', bornIn: 'New Jersey'}),
    (C:Person {name: 'Rob Reiner', bornIn: 'New York'}),
    (D:Person {name: 'Oliver Stone', bornIn: 'New York'}),
    (E:Person {name: 'Martin Sheen', bornIn: 'Ohio'})
$$) as (result agtype);

F.3.19.1. Merge Nodes

F.3.19.1.1. Merge a Node with a Label

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

SELECT * FROM cypher('graph_name', $$
MERGE (v:Critic)
RETURN v
$$) as (v agtype);

                    v
-------------------------------------------------
 {id: 0; label: 'Critic': properties:{}}::vertex
(1 row)

If there exists a vertex with the label Critic, that vertex will be returned. Otherwise, the vertex will be created and returned.

F.3.19.1.2. Merge Single Vertex with Properties

Merging a vertex node with properties where not all properties match any existing vertex.

SELECT * FROM cypher('graph_name', $$
MERGE (charlie {name: 'Charlie Sheen', age: 10})
RETURN charlie
$$) as (v agtype);

                                v
------------------------------------------------------------------------------
 {id: 0; label: 'Actor': properties:{name: 'Charlie Sheen', age: 10}}::vertex
(1 row)

If there exists a vertex with the label Critic, that vertex will be returned. Otherwise, the vertex will be created and returned.

If there exists a vertex with all properties, that vertex will be returned. Otherwise, a new vertex with the name Charlie Sheen will be created and returned.

F.3.19.1.3. Merge a Single Vertex Specifying Both Label and Property

Merging a vertex where both label and property constraints match an existing vertex.

SELECT * FROM cypher('graph_name', $$
MERGE (michael:Person {name: 'Michael Douglas'})
RETURN michael.name, michael.bornIn
$$) as (Name agtype, BornIn agtype);

       name        |    bornin
-------------------+--------------
 "Michael Douglas" | "New Jersey"
(1 row)

Michael Douglas will match the existing vertex, and the vertex name and bornIn properties will be returned.

F.3.20. Predicate Functions

Predicates are boolean functions that return true or false for a given set of input. They are most commonly used to filter out subgraphs in the WHERE part of a query.

exists(property agtype) returns agtype boolean

exists() returns true if the specified property exists in the node, relationship or map. This is different from the EXISTS clause.

SELECT *
FROM cypher('graph_name', $$
     MATCH (n)
     WHERE exists(n.surname)
     RETURN n.first_name, n.last_name
$$) as (first_name agtype, last_name agtype);

 first_name |  last_name
------------+------------
   'John'	  |   'Smith'
   'Patty'	| 'Patterson'
(2 rows)
exists(path agtype) returns agtype boolean

exists() returns true if for the given path, there already exists the given path.

SELECT *
FROM cypher('graph_name', $$
     MATCH (n)
     WHERE exists((n)-[]-({name: 'Willem Defoe'}))
     RETURN n.full_name
$$) as (full_name agtype);

  full_name
--------------
'Toby Maguire'
'Tom Holland'
(2 rows)

F.3.21. Scalar Functions

id(expression agtype) returns agtype integer

id() returns the ID of a vertex or edge.

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

 id
----
 0
 1
 2
 3
(4 rows)
start_id(expression agtype) returns agtype integer

start_id() returns the ID of the vertex that is the starting vertex for the edge.

SELECT *
FROM cypher('graph_name', $$
    MATCH ()-[e]->()
    RETURN start_id(e)
$$) as (start_id agtype);

 start_id
----------
 0
 1
 2
 3
(4 rows)
end_id(expression agtype) returns agtype integer

end_id() returns the ID of the vertex that is the ending vertex for the edge.

SELECT *
FROM cypher('graph_name', $$
    MATCH ()-[e]->()
    RETURN end_id(e)
$$) as (end_id agtype);

 end_id
--------
 4
 5
 6
 7
(4 rows)
type(edge agtype) returns agtype string

type() returns the string representation of the edge type.

SELECT *
FROM cypher('graph_name', $$
    MATCH ()-[e]->()
    RETURN type(e)
$$) as (type agtype);

 type
------
'KNOWS'
'KNOWS'
(2 rows)
properties(expression agtype) returns agtype map

properties() returns an agtype map containing all the properties of a vertex or edge. If the argument is already a map, it is returned unchanged. properties(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    CREATE (p:Person {name: 'Stefan', city: 'Berlin'})
    RETURN properties(p)
$$) as (type agtype);

                 type
--------------------------------------
 {"city": "Berlin", "name": "Stefan"}
(1 row)
head(list agtype) returns agtype

head() returns the first element in an agtype list. head(null) returns null. If the first element in the list is null, head(list) will return null.

SELECT *
FROM cypher('graph_name', $$
   MATCH (a)
   WHERE a.name = 'Eskil'
   RETURN a.array, head(a.array)
$$) as (lst agtype, lst_head agtype);

          lst          | lst_head
-----------------------+----------
 ["one","two","three"] | "one"
(1 row)
last(list agtype) returns agtype

last() returns the last element in an agtype list. last(null) returns null. If the last element in the list is null, last(list) will return null.

SELECT *
FROM cypher('graph_name', $$
MATCH (a)
WHERE a.name = 'Eskil'
RETURN a.array, last(a.array)
$$) as (lst agtype, lst_tail agtype);

          lst          | lst_tail
-----------------------+----------
 ["one","two","three"] | "three"
(1 row)
length(path agtype) returns agtype integer

length() returns the length of a path. length(null) returns null.

SELECT *
FROM cypher('graph_name', $$
   MATCH p = (a)-[]->(b)-[]->(c)
   WHERE a.name = 'Alice'
   RETURN length(p)
$$) as (length_of_path agtype);

 length_of_path
----------------
 2
 2
 2
(3 rows)
size(list variadic "any") returns agtype integer

size() returns the length of a list. size(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    RETURN size(['Alice', 'Bob'])
$$) as (size_of_list agtype);

 size_of_list
--------------
 2
(1 row)
startNode(edge agtype) returns agtype

startNode() returns the start node of an edge. startNode(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    MATCH (x:Developer)-[r]-()
    RETURN startNode(r)
$$) as (v agtype);

                  v
------------------------------------------
Node[0]{name:"Alice",age:38,eyes:"brown"}
Node[0]{name:"Alice",age:38,eyes:"brown"}
(2 rows)
endNode(edge agtype) returns agtype

endNode() returns the end node of an edge. endNode(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    MATCH (x:Developer)-[r]-()
    RETURN endNode(r)
$$) as (v agtype);

                  v
-------------------------------------------
Node[2]{name:"Charlie",age:53,eyes:"green"}
Node[1]{name:"Bob",age:25,eyes:"blue"}
(2 rows)
timestamp() returns agtype integer

timestamp() returns the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC. timestamp will return the same value during one entire query, even for long-running queries.

SELECT *
FROM cypher('graph_name', $$
    RETURN timestamp()
$$) as (t agtype);

       t
---------------
1613496720760
(1 row)
toBoolean(expression variadic "any") returns agtype boolean

toBoolean() converts a string value to a boolean value. toBoolean(null) returns null. If expression is a boolean value, it will be returned unchanged. If the parsing fails, null will be returned.

SELECT *
FROM cypher('graph_name', $$
    RETURN toBoolean('TRUE'), toBoolean('not a boolean')
$$) as (a_bool agtype, not_a_bool agtype);

 a_bool | not_a_bool
--------+------------
 true   | NULL
(1 row)
toFloat(expression variadic "any") returns agtype float

toFloat() converts an integer or string value to a floating point number. toFloat(null) returns null. If expression is a floating point number, it will be returned unchanged. If the parsing fails, null will be returned.

SELECT *
FROM cypher('graph_name', $$
    RETURN toFloat('11.5'), toFloat('not a number')
$$) as (a_float agtype, not_a_float agtype);

 a_float | not_a_float
---------+-------------
 11.5    | NULL
(1 row)
toInteger(expression variadic "any") returns agtype integer

toInteger() converts a floating point or string value to an integer value. toInteger(null) returns null. If expression is an integer value, it will be returned unchanged. If the parsing fails, null will be returned.

SELECT *
FROM cypher('graph_name', $$
     RETURN toInteger('42'), toInteger('not a number')
$$) as (an_integer agtype, not_an_integer agtype);

 an_integer | not_an_integer
------------+----------------
 42         | NULL
(1 row)
coalesce(expression agtype [, expression agtype]*) returns agtype

coalesce() returns the first non-null value in the given list of expressions. null will be returned if all the arguments are null.

SELECT *
FROM cypher('graph_name', $$
MATCH (a)
WHERE a.name = 'Alice'
RETURN coalesce(a.hairColor, a.eyes), a.hair_color, a.eyes
$$) as (color agtype, hair_color agtype, eyes agtype);

 color | hair_color |  eyes
-------+------------+--------
“brown”| NULL	      | “Brown”
(1 row)

F.3.22. List Functions

SELECT * from cypher('graph_name', $$
CREATE (A:Person {name: 'Alice', age: 38, eyes: 'brown'}),
    (B:Person {name: 'Bob', age: 25, eyes: 'blue'}),
    (C:Person {name: 'Charlie', age: 53, eyes: 'green'}),
    (D:Person {name: 'Daniel', age: 54, eyes: 'brown'}),
    (E:Person {name: 'Eskil', age: 41, eyes: 'blue', array: ['one', 'two', 'three']}),
    (A)-[:KNOWS]->(B),
    (A)-[:KNOWS]->(C),
    (B)-[:KNOWS]->(D),
    (C)-[:KNOWS]->(D),
    (B)-[:KNOWS]->(E)
$$) as (result agtype);
keys(expression agtype) returns agtype list

keys() returns a list containing the string representations for all the property names of a vertex, edge, or map. keys(null) returns null.

SELECT * from cypher('graph_name', $$
    MATCH (a)
    WHERE a.name = 'Alice'
    RETURN keys(a)
$$) as (result agtype);

         result
-------------------------
 ["age", "eyes", "name"]
(1 row)

A list containing the names of all the properties on the vertex bound to a is returned.

range(start variadic "any", end variadic "any" [, step variadic "any"]) returns agtype list

range() returns a list comprising all integer values within a range bounded by a start value start and end value end, where the difference step between any two consecutive values is constant; i.e. an arithmetic progression. The range is inclusive, and the arithmetic progression will therefore always contain start and — depending on the values of start, step, and endend.

SELECT *
FROM cypher('graph_name', $$
    RETURN range(0, 10), range(2, 18, 3)
$$) as (no_step agtype, step agtype);

              no_step               |         step
------------------------------------+-----------------------
 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] | [2, 5, 8, 11, 14, 17]
(1 row)

Two lists of numbers in the given ranges are returned.

labels(vertex agtype) returns agtype list

labels() returns a list containing the string representations for all the labels of a node. labels(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    MATCH (a)
    WHERE a.name = 'Alice'
    RETURN labels(a)
$$) as (edges agtype);

   edges
------------
 ["Person"]
(1 row)

A list containing all the labels of the node bound to a is returned.

nodes(path agtype) returns agtype list

nodes() returns a list containing all the vertices in a path. nodes(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    MATCH p = (a)-[]->(b)-[]->(c)
    WHERE a.name = 'Alice' AND c.name = 'Eskil'
    RETURN nodes(p)
$$) as (vertices agtype);

                                                                                                                                                                                     vertices
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 [{"id": 844424930131969, "label": "Person", "properties": {"age": 38, "eyes": "brown", "name": "Alice"}}::vertex, {"id": 844424930131970, "label": "Person", "properties": {"age": 25, "eyes": "blue", "name": "Bob"}}::vertex, {"id": 844424930131973, "label": "Person", "properties": {"age": 41, "eyes": "blue", "name": "Eskil", "array": ["one", "two", "three"]}}::vertex]
(1 row)

A list containing all the vertices in the path p is returned.

relationships(path agtype) returns agtype list

relationships() returns a list containing all the relationships in a path. relationships(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    MATCH p = (a)-[]->(b)-[]->(c)
    WHERE a.name = 'Alice' AND c.name = 'Eskil'
    RETURN relationships(p)
$$) as (edges agtype);

                                                                                                                          edges
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 [{"id": 1125899906842625, "label": "KNOWS", "end_id": 844424930131970, "start_id": 844424930131969, "properties": {}}::edge, {"id": 1125899906842629, "label": "KNOWS", "end_id": 844424930131973, "start_id": 844424930131970, "properties": {}}::edge]
(1 row)

A list containing all the edges in the path p is returned.

toBooleanList(list variadic "any") returns agtype list

toBooleanList() converts a list of values and returns a list of boolean values. If any values are not convertible to boolean they will be null in the list returned. Any null element in list is preserved. Any boolean value in list is preserved. If the list is null, null will be returned.

SELECT * FROM cypher('expr', $$
    RETURN toBooleanList(['true', 'false', 'true'])
$$) AS (toBooleanList agtype);

   toBooleanList
--------------------
[true, false, true]
(1 row)

F.3.23. Numeric Functions

rand() returns agtype float

rand() returns a random floating point number in the range from 0 (inclusive) to 1 (exclusive); i.e.[0,1). The numbers returned follow an approximate uniform distribution.

SELECT *
FROM cypher('graph_name', $$
    RETURN rand()
$$) as (random_number agtype);

   random_number
-------------------
0.3586784748902053
(1 row)
abs(list variadic "any") returns agtype

abs() returns the absolute value of the given number. abs(null) returns null. If expression is negative, -(expression) (i.e. the negation of expression) is returned.

SELECT *
FROM cypher('graph_name', $$
    MATCH (a), (e) WHERE a.name = 'Alice' AND e.name = 'Eskil'
    RETURN a.age, e.age, abs(a.age - e.age)
$$) as (alice_age agtype, eskil_age agtype, difference agtype);

 alice_age | eskil_age | difference
-----------+-----------+------------
 38        | 41        | 3
(1 row)

The absolute value of the age difference is returned.

ceil(expression variadic "any") returns agtype float

ceil() returns the smallest floating point number that is greater than or equal to the given number and equal to a mathematical integer. ceil(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    RETURN ceil(0.1)
$$) as (ceil_value agtype);

 ceil_value
------------
 1.0
(1 row)

The ceiling of 0.1 is returned.

floor(expression variadic "any") returns agtype float

floor() returns the greatest floating point number that is less than or equal to the given number and equal to a mathematical integer. floor(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    RETURN floor(0.1)
$$) as (flr agtype);

 flr
-----
 0.0
(1 row)

The floor of 0.1 is returned.

round(expression variadic "any") returns agtype float

round() returns the value of the given number rounded to the nearest integer. round(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    RETURN round(3.141592)
$$) as (rounded_value agtype);

 rounded_value
---------------
 3.0
(1 row)
sign(expression variadic "any") returns agtype integer

sign() returns the signum of the given number: 0 if the number is 0, -1 for any negative number, and 1 for any positive number. sign(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    RETURN sign(-17), sign(0.1), sign(0)
$$) as (negative_sign agtype, positive_sign agtype, zero_sign agtype);

 negative_sign | positive_sign | zero_sign
---------------+---------------+-----------
 -1            | 1             | 0
(1 row)

The signs of -17 and 0.1 are returned.

F.3.24. Logarithmic Functions

e() returns agtype float

e() returns the base of the natural logarithm, e.

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

         e
-------------------
 2.718281828459045
(1 row)
sqrt(expression variadic "any") returns agtype float

sqrt() returns the square root of a number.

SELECT *
FROM cypher('graph_name', $$
    RETURN sqrt(144)
$$) as (results agtype);

 results
---------
 12.0
(1 row)
exp(expression variadic "any") returns agtype float

exp() returns e^n, where e is the base of the natural logarithm, and n is the value of the argument expression. exp(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    RETURN exp(2)
$$) as (e agtype);

        e
------------------
 7.38905609893065
(1 row)

e to the power of 2 is returned.

log(expression variadic "any") returns agtype float

log() returns the natural logarithm of a number. log(null) returns null. log(0) returns null.

SELECT *
FROM cypher('graph_name', $$
    RETURN log(27)
$$) as (natural_logarithm agtype);

 natural_logarithm
-------------------
 3.295836866004329
(1 row)

The natural logarithm of 27 is returned.

log10(expression variadic "any") returns agtype float

log10() returns the common logarithm (base 10) of a number. log10(null) returns null. log10(0) returns null.

SELECT *
FROM cypher('graph_name', $$
    RETURN log10(27)
$$) as (common_logarithm agtype);

  common_logarithm
--------------------
 1.4313637641589874
(1 row)

The common logarithm of 27 is returned.

F.3.25. Trigonometric Functions

degrees(expression variadic "any") returns agtype float

degrees() converts radians to degrees. degrees(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    RETURN degrees(3.14159)
$$) as (deg agtype);

        deg
-------------------
 179.9998479605043
(1 row)

The number of degrees close to pi is returned.

radians(expression variadic "any") returns agtype float

radians() converts degrees to radians. radians(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    RETURN radians(180)
$$) as (rad agtype);

        rad
-------------------
 3.141592653589793
(1 row)

The number of degrees close to pi is returned.

pi() returns agtype float

pi() returns the mathematical constant pi.

SELECT *
FROM cypher('graph_name', $$
    RETURN pi()
$$) as (p agtype);

         p
-------------------
 3.141592653589793
(1 row)

The constant pi is returned.

sin(expression variadic "any") returns agtype float

sin() returns the sine of a number. sin(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    RETURN sin(0.5)
$$) as (s agtype);

         s
-------------------
 0.479425538604203
(1 row)

The sine of 0.5 is returned.

cos(expression variadic "any") returns agtype float

cos() returns the sine of a number. cos(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    RETURN cos(0.5)
$$) as (c agtype);

         c
--------------------
 0.8775825618903728
(1 row)

The cosine of 0.5 is returned.

tan(expression variadic "any") returns agtype float

tan() returns the tangent of a number. tan(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    RETURN tan(0.5)
$$) as (t agtype);

         t
--------------------
 0.5463024898437905
(1 row)

The tangent of 0.5 is returned.

cot(expression variadic "any") returns agtype float

cot() returns the cotangent of a number. cot(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    RETURN cot(0.5)
$$) as (t agtype);

         t
-------------------
 1.830487721712452
(1 row)

The cotangent of 0.5 is returned.

asin(expression variadic "any") returns agtype float

asin() returns the arcsine of a number. asin(null) returns null. If (expression < -1) or (expression > 1), then asin(expression) returns null.

SELECT *
FROM cypher('graph_name', $$
    RETURN asin(0.5)
$$) as (arc_s agtype);

       arc_s
--------------------
 0.5235987755982989
(1 row)

The arcsine of 0.5 is returned.

acos(expression variadic "any") returns agtype float

acos() returns the arcsine of a number. acos(null) returns null. If (expression < -1) or (expression > 1), then acos(expression) returns null.

SELECT *
FROM cypher('graph_name', $$
    RETURN acos(0.5)
$$) as (arc_c agtype);

       arc_c
--------------------
 1.0471975511965979
(1 row)

The arccosine of 0.5 is returned.

atan(expression variadic "any") returns agtype float

atan() returns the arctangent of a number. atan(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    RETURN atan(0.5)
$$) as (arc_t agtype);

       arc_t
--------------------
 0.4636476090008061
(1 row)

The arctangent of 0.5 is returned.

atan2(expression1 variadic "any", expression2 variadic "any") returns agtype float

atan2() returns the arctangent of a set of coordinates in radians. atan2(null, null), atan2(null, expression2) and atan(expression1, null) all return null.

SELECT *
FROM cypher('graph_name', $$
    RETURN atan2(0.5, 0.6)
$$) as (arc_t2 agtype);

       arc_t2
--------------------
 0.6947382761967033
(1 row)

The arctangent of 0.5 and 0.6 is returned.

F.3.26. String Functions

replace(original, search variadic "any", replace variadic "any") returns agtype string

replace() returns a string in which all occurrences of a specified string in the original string have been replaced by another (specified) string. If any argument is null, null will be returned. If search is not found in original, original will be returned.

SELECT *
FROM cypher('graph_name', $$
    RETURN replace('hello', 'l', 'w')
$$) as (str_array agtype);

 str_array
-----------
 "hewwo"
(1 row)
split(original variadic "any", split_delimiter variadic "any") returns list of agtype strings

split() returns a list of strings resulting from the splitting of the original string around matches of the given delimiter. split(null, splitDelimiter) and split(original, null) both return null.

SELECT *
FROM cypher('graph_name', $$
    RETURN split('one,two', ',')
$$) as (split_list agtype);

   split_list
----------------
 ["one", "two"]
(1 row)
left(original variadic "any", length variadic "any") returns agtype string

left() returns a string containing the specified number of leftmost characters of the original string. left(null, length) and left(null, null) both return null. left(original, null) will raise an error. If length is not a positive integer, an error is raised. If length exceeds the size of original, original is returned.

SELECT *
FROM cypher('graph_name', $$
    RETURN left('Hello', 3)
$$) as (new_str agtype);

 new_str
---------
 "Hel"
(1 row)
right(original variadic "any", length variadic "any") returns agtype string

right() returns a string containing the specified number of rightmost characters of the original string. right(null, length) and right(null, null) both return null. right(original, null) will raise an error. If length is not a positive integer, an error is raised. If length exceeds the size of original, original is returned.

SELECT *
FROM cypher('graph_name', $$
    RETURN right('hello', 3)
$$) as (new_str agtype);

 new_str
---------
 "llo"
(1 row)
substring(original variadic "any", start variadic "any" [, length variadic "any"]) returns agtype string

substring() returns a substring of the original string, beginning with a 0-based index start and length. start uses a zero-based index. If length is omitted, the function returns the substring starting at the position given by start and extending to the end of original. If original is null, null is returned. If either start or length is null or a negative integer, an error is raised. If start is 0, the substring will start at the beginning of original. If length is 0, the empty string will be returned.

SELECT *
FROM cypher('graph_name', $$
    RETURN substring('hello', 1, 3), substring('hello', 2)
$$) as (sub_str1 agtype, sub_str2 agtype);

 sub_str1 | sub_str2
----------+----------
 "ell"    | "llo"
(1 row)
rTrim(original variadic "any") returns agtype string

rTrim() returns the original string with trailing whitespace removed. rTrim(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    RETURN rTrim(' hello ')
$$) as (right_trimmed_str agtype);

 right_trimmed_str
-------------------
 " hello"
(1 row)
lTrim(original variadic "any") returns agtype string

lTrim() returns the original string with leading whitespace removed. lTrim(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    RETURN lTrim(' hello ')
$$) as (left_trimmed_str agtype);

 left_trimmed_str
------------------
 "hello "
(1 row)
trim(original variadic "any") returns agtype string

trim() returns the original string with leading and trailing whitespace removed. trim(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    RETURN trim(' hello ')
$$) as (trimmed_str agtype);

 trimmed_str
-------------
 "hello"
(1 row)
toLower(original variadic "any") returns agtype string

toLower() returns the original string in lowercase. toLower(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    RETURN toLower('HELLO')
$$) as (lower_str agtype);

 lower_str
-----------
 "hello"
(1 row)
toUpper(original variadic "any") returns agtype string

toUpper() returns the original string in uppercase. toUpper(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    RETURN toUpper('hello')
$$) as (upper_str agtype);

 upper_str
-----------
 "HELLO"
(1 row)
reverse(original variadic "any") returns agtype string

reverse() returns a string in which the order of all characters in the original string have been reversed. reverse(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    RETURN reverse('hello')
$$) as (reverse_str agtype);

 reverse_str
-------------
 "olleh"
(1 row)
toString(expression agtype) returns string

toString() converts an integer, float or boolean value to a string. toString(null) returns null. If expression is a string, it will be returned unchanged.

SELECT *
FROM cypher('graph_name', $$
    RETURN toString(11.5),toString('a string'), toString(true)
$$) as (float_to_str agtype, str_to_str agtype, bool_to_string agtype);

 float_to_str | str_to_str | bool_to_string
--------------+------------+----------------
 "11.5"       | "a string" | "true"
(1 row)

F.3.27. Aggregation Functions

Functions that activate auto aggregation.

LOAD 'age';
SET search_path TO ag_catalog;

SELECT create_graph('graph_name');

SELECT * FROM cypher('graph_name', $$
    CREATE (a:Person {name: 'A', age: 13}),
    (b:Person {name: 'B', age: 33, eyes: 'blue'}),
    (c:Person {name: 'C', age: 44, eyes: 'blue'}),
    (d1:Person {name: 'D', eyes: 'brown'}),
    (d2:Person {name: 'D'}),
    (a)-[:KNOWS]->(b),
    (a)-[:KNOWS]->(c),
    (a)-[:KNOWS]->(d1),
    (b)-[:KNOWS]->(d2),
    (c)-[:KNOWS]->(d2)
$$) as (a agtype);
min(expression variadic "any") returns agtype

min() returns the minimum value in a set of values. Any null values are excluded from the calculation. In a mixed set, any string value is always considered to be lower than any numeric value, and any list is always considered to be lower than any string. Lists are compared in dictionary order, i.e. list elements are compared pairwise in ascending order from the start of the list to the end. min(null) returns null.

SELECT *
FROM cypher('graph_name', $$
    MATCH (v:Person)
    RETURN min(v.age)
$$) as (min_age agtype);

 min_age
---------
 13
(1 row)

The lowest of all the values in the property age is returned.

To clarify the following example, assume the next three commands are run first:

SELECT * FROM cypher('graph_name', $$
    CREATE (:min_test {val:'d'})
$$) as (result agtype);

SELECT * FROM cypher('graph_name', $$
    CREATE (:min_test {val:['a', 'b', 23]})
$$) as (result agtype);

SELECT * FROM cypher('graph_name', $$
    CREATE (:min_test {val:[1, 'b', 23]})
$$) as (result agtype);

The example below shows using min() with lists:

SELECT *
FROM cypher('graph_name', $$
    MATCH (v:min_test)
    RETURN min(v.val)
$$) as (min_val agtype);

    min_val
----------------
 ["a", "b", 23]
(1 row)

The lowest of all the values in the set is returned (in this case, the list ["a", "b", 23]), as the two lists are considered to be lower values than the string "d", and the string "a" is considered to be a lower value than the numerical value 1.

max(expression variadic "any") returns agtype float

max() returns the maximum value in a set of values. Any null values are excluded from the calculation. In a mixed set, any numeric value is always considered to be higher than any string value, and any string value is always considered to be higher than any list. Lists are compared in dictionary order, i.e. list elements are compared pairwise in ascending order from the start of the list to the end. max(null) returns null.

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

 max_age
---------
 44
(1 row)

The highest of all the values in the property age is returned.

stDev(expression variadic "any") returns agtype float

stDev() returns the standard deviation for the given value over a group. It uses a standard two-pass method, with N - 1 as the denominator, and should be used when taking a sample of the population for an unbiased estimate. When the standard deviation of the entire population is being calculated, stDevP should be used. Any null values are excluded from the calculation. stDev(null) returns 0.0 (zero).

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

     stdev_age
--------------------
 15.716233645501712
(1 row)

The standard deviation of the values in the property age is returned.

stDevP(expression variadic "any") returns agtype float

stDev() returns the standard deviation for the given value over a group. It uses a standard two-pass method, with N as the denominator, and should be used when calculating the standard deviation for an entire population. When the standard deviation of only a sample of the population is being calculated, stDev should be used. Any null values are excluded from the calculation. stDevP(null) returns 0.0 (zero).

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

     stdevp_age
--------------------
 12.832251036613439
(1 row)

The population standard deviation of the values in the property age is returned.

percentileCont(expression agtype, percentile agtype) returns agtype float

percentileCont() returns the percentile of the given value over a group, with a percentile from 0.0 to 1.0. It uses a linear interpolation method, calculating a weighted average between two values if the desired percentile lies between them. For nearest values using a rounding method, see percentileDisc. Any null values are excluded from the calculation. percentileCont(null, percentile) returns null.

SELECT *
FROM cypher('graph_name', $$
    MATCH (n:Person)
    RETURN percentileCont(n.age, 0.4)
$$) as (percentile_cont_age agtype);

 percentile_cont_age
---------------------
 29.0
(1 row)

The 40th percentile of the values in the property age is returned, calculated with a weighted average. In this case, 0.4 is the median, or 40th percentile.

percentileDisc(expression agtype, percentile agtype) returns agtype float

percentileDisc() returns the percentile of the given value over a group, with a percentile from 0.0 to 1.0. It uses a rounding method and calculates the nearest value to the percentile. For interpolated values, see percentileCont. Any null values are excluded from the calculation. percentileDisc(null, percentile) returns null.

SELECT *
FROM cypher('graph_name', $$
    MATCH (n:Person)
    RETURN percentileDisc(n.age, 0.5)
$$) as (percentile_disc_age agtype);

 percentile_disc_age
---------------------
 33.0
(1 row)

The 50th percentile of the values in the property age is returned.

count(expression agtype) returns agtype integer

count() returns the number of values or records, and appears in two variants:

  • count(*) returns the number of matching records.

  • count(expr) returns the number of non-null values returned by an expression.

count(*) includes records returning null. count(expr) ignores null values. count(null) returns 0 (zero). count(*) can be used to return the number of nodes; for example, the number of nodes connected to some node n.

SELECT *
FROM cypher('graph_name', $$
    MATCH (n {name: 'A'})-[]->(x)
    RETURN n.age, count(*)
$$) as (age agtype, number_of_people agtype);

 age | number_of_people
-----+------------------
 13  | 3
(1 row)

The age property of the start node n (with a name value of "A") and the number of nodes related to n are returned.

count(*) can be used to group and count relationship types, returning the number of relationships of each type.

SELECT *
FROM cypher('graph_name', $$
    MATCH (n {name: 'A'})-[r]->()
    RETURN type(r), count(*)
$$) as (label agtype, count agtype);

  label  | count
---------+-------
 "KNOWS" | 3
(1 row)

The relationship type and the number of relationships with that type are returned.

Instead of simply returning the number of records with count(*), it may be more useful to return the actual number of values returned by an expression.

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

 count
-------
 3
(1 row)

The number of nodes connected to the start node n is returned.

count(expression) can be used to return the number of non-null values returned by the expression.

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

 count
-------
 3
(1 row)

The number of nodes with the label Person that have a non-null value for the age property is returned.

In the following example we are trying to find all our friends of friends, and count them. The first aggregate function, count(DISTINCT friend_of_friend), will only count a friend_of_friend once, as DISTINCT removes the duplicates. The second aggregate function, count(friend_of_friend), will consider the same friend_of_friend multiple times.

SELECT *
FROM cypher('graph_name', $$
    MATCH (me:Person)-[]->(friend:Person)-[]->(friend_of_friend:Person)
    WHERE me.name = 'A'
    RETURN count(DISTINCT friend_of_friend), count(friend_of_friend)
$$) as (friend_of_friends_distinct agtype, friend_of_friends agtype);

 friend_of_friends_distinct | friend_of_friends
----------------------------+-------------------
 1                          | 2
(1 row)

Both B and C know D and thus D will get counted twice when not using DISTINCT.

avg(expression agtype) returns agtype integer

avg() returns the average of a set of numeric values. Any null values are excluded from the calculation. avg(null) returns null.

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

 avg_age
---------
 30.0
(1 row)

The average of all the values in the property age is returned.

sum(expression agtype) returns agtype float

sum() returns the sum of a set of numeric values. Any null values are excluded from the calculation. sum(null) returns null.

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

 total_age
-----------
 90
(1 row)

The sum of all the values in the property age is returned.

F.3.28. User-Defined Functions

Users may add custom functions to apache_age. When using Cypher functions, all function calls with a Cypher query use the default namespace of: ag_catalog. However, if a user wants to use a function outside of this namespace, they may do so by adding the namespace before the function name.

Syntax: namespace_name.function_name

SELECT *
FROM cypher('graph_name', $$
RETURN pg_catalog.sqrt(25)
$$) as (result agtype);

 result
--------
 25
(1 row)

F.3.29. apache_age Beyond Cypher

All queries so far have followed the same pattern: a SELECT clause followed by a single Cypher call in the FROM clause. However, a Cypher query can be used in many other ways. This section highlights some more advanced ways of using the Cypher call within a more complex SQL/Cypher Hybrid Query.

F.3.29.1. Using Cypher in a CTE Expression

There are no restrictions to using Cypher with CTEs (Common Table Expression).

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

 name      | age
-----------+-----
 "Andres"  | 36
 "Tobias"  | 25
 "Peter"   | 35
(3 rows)

F.3.29.2. Using Cypher in a Join expression

A Cypher query can be part of a JOIN clause.

Note

Cypher queries using the CREATE, SET, REMOVE clauses cannot be used in SQL queries with joins, as they affect the Postgres Pro transaction system. One possible solution is to protect the query with CTEs.

SELECT id,
    graph_query.name = t.name as names_match,
    graph_query.age = t.age as ages_match
FROM schema_name.sql_person AS t
JOIN cypher('graph_name', $$
        MATCH (n:Person)
        RETURN n.name, n.age, id(n)
$$) as graph_query(name agtype, age agtype, id agtype)
ON t.person_id = graph_query.id;

 id | names_match | ages_match
 ---+-------------+------------
 1  | True        | True
 2  | False       | True
 3  | True        | False
(3 rows)

F.3.29.3. Cypher in SQL Expressions

Cypher cannot be used in an expression, the query must exists in the FROM clause of a query. However, if the Cypher query is placed in a subquery, it will behave as any SQL style query.

F.3.29.3.1. Using Cypher with =

When writing a Cypher query that is known to return one column and one row, the = comparison operator may be used.

SELECT t.name FROM schema_name.sql_person AS t
where t.name = (
    SELECT a
    FROM cypher('graph_name', $$
          MATCH (v)
        RETURN v.name
    $$) as (name varchar(50))
    ORDER BY name
    LIMIT 1);

   name   | age
----------+-----
 "Andres" | 36
(1 rows)
F.3.29.3.2. Working with Postgres Pro IN Clause

When writing a Cypher query that is known to return one column but may have multiple rows, the IN operator may be used.

SELECT t.name, t.age FROM schema_name.sql_person as t
where t.name in (
    SELECT *
    FROM cypher('graph_name', $$
        MATCH (v:Person)
        RETURN v.name
    $$) as (a agtype));

  name     | age
-----------+-----
 "Andres"  | 36
 "Tobias"  | 25
 "Peter"   | 35
(3 rows)
F.3.29.3.3. Working with Postgres Pro EXISTS Clause

When writing a Cypher query that may have more than one column and row returned, the EXISTS operator may be used.

SELECT t.name, t.age
FROM schema_name.sql_person as t
WHERE EXISTS (
    SELECT *
    FROM cypher('graph_name', $$
      MATCH (v:Person)
        RETURN v.name, v.age
    $$) as (name agtype, age agtype)
    WHERE name = t.name AND age = t.age
);

   name    | age
-----------+-----
 "Andres"  | 36
 "Tobias"  | 25
 "Peter"   | 35
(3 rows)
F.3.29.3.4. Querying Multiple Graphs

There is no restriction to the number of graphs an SQL statement can query. Users may query multiple graphs simultaneously.

SELECT graph_1.name, graph_1.age, graph_2.license_number
FROM cypher('graph_1', $$
    MATCH (v:Person)
    RETURN v.name, v.age
$$) as graph_1(col_1 agtype, col_2 agtype, col_3 agtype)
JOIN cypher('graph_2', $$
    MATCH (v:Doctor)
    RETURN v.name, v.license_number
$$) as graph_2(name agtype, license_number agtype)
ON graph_1.name = graph_2.name

   name    | age | license_number
-----------+-----+----------------
 "Andres"  | 36  | 1234567890
(1 rows)

F.3.29.4. 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 apache_age Query Format for details.

A Cypher parameter is in the format of a "$" followed by an identifier. Unlike Postgres Pro parameters, Cypher parameters start with a letter, followed by an alphanumeric string of arbitrary length. Example: $parameter_name

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

PREPARE cypher_stored_procedure(agtype) AS
SELECT *
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);

When executing the prepared statement, place an agtype map with the parameter values where the Postgres Pro 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'}');

F.3.29.5. PL/pgSQL Functions

Cypher commands can be run in PL/pgSQL functions without restriction.

SELECT *
FROM cypher('imdb', $$
    CREATE (toby:actor {name: 'Toby Maguire'}),
        (tom:actor {name: 'Tom Holland'}),
        (willam:actor {name: 'Willam Dafoe'}),
        (robert:actor {name: 'Robert Downey Jr'}),
        (spiderman:movie {title: 'Spiderman'}),
        (no_way_home:movie {title: 'Spiderman: No Way Home'}),
        (homecoming:movie {title: 'Spiderman: Homecoming'}),
        (ironman:movie {title: 'Ironman'}),
        (tropic_thunder:movie {title: 'Tropic Thunder'}),
        (toby)-[:acted_in {role: 'Peter Parker', alter_ego: 'Spiderman'}]->(spiderman),
        (willam)-[:acted_in {role: 'Norman Osborn', alter_ego: 'Green Goblin'}]->(spiderman),
        (toby)-[:acted_in {role: 'Toby Maguire'}]->(tropic_thunder),
        (robert)-[:acted_in {role: 'Kirk Lazarus'}]->(tropic_thunder),
        (robert)-[:acted_in {role: 'Tony Stark', alter_ego: 'Ironman'}]->(homecoming),
        (tom)-[:acted_in {role: 'Peter Parker', alter_ego: 'Spiderman'}]->(homecoming),
        (tom)-[:acted_in {role: 'Peter Parker', alter_ego: 'Spiderman'}]->(no_way_home),
        (toby)-[:acted_in {role: 'Peter Parker', alter_ego: 'Spiderman'}]->(no_way_home),
        (willam)-[:acted_in {role: 'Norman Osborn', alter_ego: 'Green Goblin'}]->(no_way_home)
$$) AS (a agtype);

The example of function creation is shown below.

CREATE OR REPLACE FUNCTION get_all_actor_names()
RETURNS TABLE(actor agtype)
LANGUAGE plpgsql
AS $BODY$
BEGIN
    LOAD 'age';
    SET search_path TO ag_catalog;

    RETURN QUERY
    SELECT *
    FROM ag_catalog.cypher('imdb', $$
        MATCH (v:actor)
        RETURN v.name
    $$) AS (a agtype);
END
$BODY$;

Execute the query:

SELECT * FROM get_all_actor_names();

       actor
--------------------
 "Toby Maguire"
 "Tom Holland"
 "Willam Dafoe"
 "Robert Downey Jr"
(4 rows)

Note

It is recommended to add the LOAD 'age' command and setting the search_path to the function declaration to ensure that the CREATE FUNCTION command works consistently.

F.3.29.5.1. Dynamic Cypher Example
CREATE OR REPLACE FUNCTION get_actors_who_played_role(role agtype)
RETURNS TABLE(actor agtype, movie agtype)
LANGUAGE plpgsql
AS $function$
DECLARE sql VARCHAR;
BEGIN
        load 'age';
        SET search_path TO ag_catalog;

        sql := format('
        SELECT *
        FROM cypher(''imdb'', $$
            MATCH (actor)-[:acted_in {role: %s}]->(movie:movie)
            RETURN actor.name, movie.title
        $$) AS (actor agtype, movie agtype);
    ', role);

        RETURN QUERY EXECUTE sql;

END
$function$;
SELECT * FROM get_actors_who_played_role('"Peter Parker"');

     actor      |          movie
----------------+--------------------------
 "Toby Maguire" | "Spiderman: No Way Home"
 "Toby Maguire" | "Spiderman"
 "Tom Holland"  | "Spiderman: Homecoming"
 "Tom Holland"  | "Spiderman: No Way Home"
(4 rows)

F.3.29.6. SQL in Cypher

apache_age does not support SQL being directly written in Cypher. However, with user-defined functions you can write SQL queries and call them in a Cypher command.

Note

This applies to void and scalar-value functions only. Set returning functions are not currently supported.

Create a function:

CREATE OR REPLACE FUNCTION public.get_event_year(name agtype) returns agtype AS $$
    SELECT year::agtype
    FROM history AS h
    WHERE h.event_name = name::text
    LIMIT 1;
$$ LANGUAGE sql;
SELECT * FROM cypher('graph_name', $$
    MATCH (e:event)
    WHERE e.year < public.get_event_year(e.name)
    RETURN e.name
$$) as (n agtype);

        n
-------------------
 "Apache Con 2021"
(1 row)