Начинается кластер про чтение данных. В уроках про CREATE и MERGE мы строили граф сами, а теперь возвращаемся к готовому графу книжного клуба. Первая пара клауз, без которой не обходится ни один запрос: MATCH находит, RETURN возвращает. Сегодня учимся управлять тем, что именно вернётся: какие колонки, под какими именами, в каком порядке и сколько строк.
Учебный граф
Во всех песочницах кластера лежит один и тот же граф:
- десять участников с меткой
Person, свойстваnameиborn, у Егора год рождения не указан, Яна ни с кем не связана; - три города
Cityсо свойствомname; - пять книг
Bookсо свойствамиtitle,author,year; - связи
KNOWS {since},LIVES_INиREAD {rating}, у одного отзыва Ильи оценки нет.
Вернуть узлы целиком
Самый простой запрос: найти узлы по метке и вернуть их.
MATCH (c:City)
RETURN c;
+--------------------------+
| c |
+--------------------------+
| (:City {name: "Москва"}) |
| (:City {name: "Казань"}) |
| (:City {name: "Пермь"}) |
+--------------------------+
3 rows
MATCH (c:City) находит все узлы с меткой City и называет каждый c. RETURN c отдаёт их целиком, с метками и свойствами. Это удобно, чтобы осмотреться, но в приложении обычно нужны отдельные свойства.
Вернуть свойства
К свойству обращаются через точку: p.name, p.born. Колонка в ответе называется так же, как выражение:
MATCH (p:Person)
RETURN p.name, p.born;
+------------------+
| p.name | p.born |
+------------------+
| "Аня" | 1994 |
| "Борис" | 1990 |
| "Вера" | 1998 |
| "Глеб" | 1987 |
| "Дина" | 2001 |
| "Егор" | NULL |
| "Жанна" | 1992 |
| "Зоя" | 1999 |
| "Илья" | 1985 |
| "Яна" | 2003 |
+------------------+
10 rows
У Егора года рождения нет, и в его строке NULL. Отсутствующее свойство в Cypher читается как null, ошибки не будет. Про null подробнее в уроке про OPTIONAL MATCH.
Порядок строк здесь совпал с порядком, в котором участники попали в базу. Но это случайность реализации, а не обещание: без ORDER BY база вправе вернуть строки в любом порядке.
Псевдонимы и выражения
AS даёт колонке короткое имя. В RETURN можно писать не только свойства, но и выражения, например возраст книги:
MATCH (b:Book)
RETURN b.title AS title, 2026 - b.year AS age
ORDER BY age DESC;
+------------------------------------------+
| title | age |
+------------------------------------------+
| "Обломов" | 167 |
| "Двенадцать стульев" | 98 |
| "Понедельник начинается в субботу" | 61 |
| "Мастер и Маргарита" | 59 |
| "Пикник на обочине" | 54 |
+------------------------------------------+
5 rows
После AS имя age можно использовать в ORDER BY. Это псевдоним: имя колонки результата.
ORDER BY, SKIP и LIMIT
ORDER BY сортирует, DESC переворачивает порядок. LIMIT оставляет первые строки, SKIP пропускает строки с начала. Три самых молодых участника:
MATCH (p:Person)
RETURN p.name AS name, p.born AS born
ORDER BY born DESC
LIMIT 3;
+---------------+
| name | born |
+---------------+
| "Егор" | NULL |
| "Яна" | 2003 |
| "Дина" | 2001 |
+---------------+
3 rows
Первым оказался Егор, у которого года нет. В Cypher null при сортировке считается больше любого значения, поэтому при DESC он всплывает наверх, а при обычной сортировке уходит в конец. Как отсеять такие строки, покажет урок про WHERE.
SKIP и LIMIT вместе дают страницы. Вторая страница по три имени в алфавитном порядке:
MATCH (p:Person)
RETURN p.name
ORDER BY p.name
SKIP 2 LIMIT 3;
+--------+
| p.name |
+--------+
| "Вера" |
| "Глеб" |
| "Дина" |
+--------+
3 rows
DISTINCT: без повторов
Книг пять, а авторов меньше: у Стругацких две книги. DISTINCT убирает одинаковые строки:
MATCH (b:Book)
RETURN DISTINCT b.author AS author
ORDER BY author;
+-----------------+
| author |
+-----------------+
| "Булгаков" |
| "Гончаров" |
| "Ильф и Петров" |
| "Стругацкие" |
+-----------------+
4 rows
DISTINCT сравнивает строки целиком. Если вернуть DISTINCT b.author, b.year, строк снова станет пять: пары автор и год у всех книг разные.
Проекция карты
Иногда удобнее вернуть одно значение со всеми нужными полями, например для JSON в приложении. Для этого есть проекция карты:
MATCH (b:Book)
RETURN b {.title, .year} AS card
ORDER BY b.year
LIMIT 2;
+-------------------------------------------+
| card |
+-------------------------------------------+
| {title: "Обломов", year: 1859} |
| {title: "Двенадцать стульев", year: 1928} |
+-------------------------------------------+
2 rows
Запись b {.title, .year} берёт у узла только два свойства. b {.*} взяла бы все, а b {.title, decade: b.year / 10 * 10} добавила бы своё поле.
Попробуй сам
Запрос возвращает пути от читателей к книгам Стругацких. Картинка покажет, кто какую из двух книг читал.
Перепиши запрос, чтобы он возвращал не пути, а имя читателя и название книги с псевдонимами reader и book, отсортированные по читателю. Потом выведи только первых двух.
Проверь себя
Задание
Дальше
Пока мы возвращали всех подряд или выбирали по точному значению свойства. В следующем уроке добавим WHERE: сравнения, диапазоны, поиск по части строки и условия на связи.
домашка