Сравнение, равенство, сортировка и эквивалентность (— Появится скоро —)

Типы данных

AGE использует пользовательский тип данных agtype, который является единственным типом, возвращаемым функциями AGE. Тип agtype представляет собой надмножество JSON и является собственной реализацией типа jsonb из PostgreSQL.

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

Null

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

Формат ввода/вывода

Запрос

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

null отображается как пустое поле.

Результат

null_result

(1 строка)

agtype NULL и PostgreSQL NULL

Понятие NULL в agtype и в PostgreSQL идентично понятию null в Cypher.

Целое число (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 строка)    

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

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

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

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

Значения, слишком большие или слишком малые, вызывают ошибку. Слишком точные входные данные могут быть округлены. Значения, слишком близкие к нулю для различимого представления, приводят к ошибке переполнения вниз (underflow).

Помимо обычных числовых значений, float поддерживает несколько специальных значений:

  • Infinity
  • -Infinity
  • NaN

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

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

Строки-литералы для этих значений сравниваются без учёта регистра.

Формат ввода/вывода

Для указания значения float необходимо включить десятичную точку.

Запрос

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

Результат

| float_result |
| ------------ |
| 1.0          |
(1 строка)      

MXnumeric

— Появится скоро —

Логический тип (Bool)

AGE предоставляет стандартный логический тип Cypher. Тип boolean имеет три возможных состояния: true, false и третье состояние — unknown, которое представляется значением agtype null.

Логические литералы записываются в запросах Cypher с помощью ключевых слов TRUE, FALSE и NULL.

Формат ввода/вывода

Запрос

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

В отличие от PostgreSQL, AGE выводит полные слова (true, false), а не односимвольные сокращения (t, f).

Результат

| boolean_result |
| -------------- |
| true           |
(1 строка)        

Строка (String)

Литералы строк agtype поддерживают следующие последовательности экранирования:

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

Формат ввода/вывода

Строки заключаются в одинарные кавычки ('). На выходе используются двойные кавычки (").

Запрос

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

Результат

| string_result      |
| ------------------ |
| "This is a string" |
(1 строка)            

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

Список (List)

Все примеры используют предложения 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 строка)                            

NULL в списках

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

Запрос

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

Результат

| lst     |
| ------- |
| [null]  |
(1 строка) 

Доступ к отдельным элементам

Для доступа к отдельным элементам списка используются квадратные скобки. Синтаксис list[start..end] возвращает элементы с индекса start (включительно) по индекс end (исключительно).

Запрос

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 строка) 

Карта как элемент списка

Запрос

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 строка)                                               

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

Запрос

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 строка)     

Отрицательные индексы

Отрицательные индексы отсчитываются от конца списка.

Запрос

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 строка) 

Диапазоны индексов

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

Запрос

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 строка)   

Диапазоны с отрицательными индексами

Запрос

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 строка)            

Срезы вперёд

Пропуск начального индекса подразумевает значение 0.

Запрос

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 строка)      

Срезы назад

Пропуск конечного индекса подразумевает длину списка.

Запрос

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 строка)

Срезы с выходом за границы усекаются без предупреждения. При обращении к отдельному элементу за пределами границ возвращается 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 строка) 

Запрос

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 строка)             

Карта (Map)

Карты можно создавать непосредственно в 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 строка)

Литералы карт с использованием составных типов данных

Карты могут содержать составные типы — например, списки и вложенные карты.

Запрос

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 строка)                                                                 

Доступ к свойствам карт

Запрос

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 строка) 

Доступ к элементам списков внутри карт

Запрос

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 строка)           

Простые сущности

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

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

GraphId

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

graphid по своей сути представляет собой знаковое 64-битное целое число (int64).

64 бита разбиты следующим образом:

<--- 16 бит ---> <----------- 48 бит ----------->
Идентификатор метки Номер записи
(биты 63–48) (биты 47–0)
  • Идентификатор метки: старшие 16 бит, диапазон от 1 до 65535 (PG_UINT16_MAX). Значение 0 недопустимо.
  • Номер записи: младшие 48 бит, диапазон от 1 до 281474976710655 (0x0000ffffffffffff). Значение 0 недопустимо.

Метки (Labels)

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

  • Рёбра должны иметь метку; вершины не обязаны иметь метку.
  • Имена меток должны быть уникальны в пределах вершин и рёбер.

Сведения о создании сущностей с метками см. в разделе о предложении CREATE.

Примечание!
В YMatrix AGE метки реализованы как таблицы базы данных — то есть одна метка соответствует ровно одной таблице.

Свойства (Properties)

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

Вершина (Vertex)

  • vertex — это фундаментальная сущность графа, обладающая самостоятельным существованием и уникальной идентичностью.
  • Вершине может быть назначена метка.
  • Из вершины может исходить ноль или более рёбер.
  • В вершину может входить ноль или более рёбер.

Формат данных

Имя поля Описание
id graphid вершины
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 строка)                                                          

Ребро (Edge)

edge — это направленное соединение между двумя узлами: исходным и целевым. Исходящее ребро рассматривается с точки зрения исходного узла; входящее ребро — с точки зрения целевого узла. Каждому ребру назначается ровно один тип ребра.

Формат данных

Имя поля Описание
id graphid ребра
startid graphid исходного узла
endid graphid целевого узла
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 строка)                                                                                    

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

Путь (Path)

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 строка)