Neo4j Cypherマニュアルv4.4 1:はじめに

[The Neo4j Cypher Manual v4.4](https://neo4j.com/docs/cypher-manual/4.4/)の日本語訳です。正確な内容については必ず原文で確認を取るようにしてください。
また、順次訳しているため、リンク先が正しくないことがあります。
#
# Neo4j Cypherマニュアルv4.4
🄫 2022 Neo4j, Inc.

ドキュメントのライセンスは[Creative Commons 4.0](https://neo4j.com/docs/license/)です。

:::note info
これは、Neo4jチームによって作成されたNeo4jバージョン4.4のためのCypherマニュアルです。
:::

このマニュアルは、以下の領域をカバーしています。

* [はじめに](#はじめに) - Cypherクエリ言語の紹介
* [構文](https://qiita.com/imai01/items/9e29c4b944c9e9f60a9a) - Cypherクエリの構文の学習
* [句](https://qiita.com/imai01/items/ac8807bb8cf4291a62bd) - Cypherクエリ句のリファレンス
* 関数 - Cypherクエリ関数のリファレンス
* 検索性能のためのインデックス - 検索性能のために使われるインデックスの管理法
* フルテキスト検索インデックス - フルテキスト検索を可能にする、フルテキストインデックスの使用法
* 制約 - データインテグリティを確保するために使用される制約の管理法
* データベース管理 - Neo4jデータベースを管理するためのCypherの使用法
* データベースエイリアス管理 - Neo4jにおけるデータベースエイリアスを管理するためのCypherの使用法
* アクセスコントロール - ロールベースのアクセス・コントロールときめ細かなセキュリティの管理法
* クエリのチューニング - クエリの分析と、性能のためにクエリをチューニングする方法の学習
* 実行プラン - Cypher実行の計画と演算子
* 非推奨、追加、互換性 - バージョン間の言語開発の概要
* キーワードの用語集 - Cypherのキーワードの用語集と、Cypherのマニュアルの他の部分へのリンク
* Cypherスタイルガイド - Cypherクエリを記述する際の推奨スタイルのガイド。

このマニュアルは、Neo4jクライアント・アプリケーションの開発者向けに書かれています。

# はじめに
:::note info
本章では、問い合わせ言語Cypherを紹介します。
:::

## Cypherとは？
Cypherは、グラフの[問い合わせ、更新、管理](#問い合わせ、更新、管理)を表現力豊かに効率的に行うことができる宣言型グラフ問い合わせ言語です。開発者と運用担当者の双方に適した設計になっています。Cypherはシンプルでありながら強力に設計されています。非常に複雑なデータベースクエリを簡単に表現できるため、データベースアクセスに迷うことなく、自分のドメインに集中することができます。

Cypherは、表現力豊かなクエリの慣習に基づく、多くの異なるアプローチやビルドから着想を得ています。WHEREやORDER BYといったキーワードの多くは、[SQL](http://ja.wikipedia.org/wiki/SQL)からヒントを得ています。パターンマッチは[SPARQL](https://ja.wikipedia.org/wiki/SPARQL)から表現のアプローチを借用しています。リストの意味合いはHaskellやPythonのような言語から借用したものです。Cypherの構造は英語の散文と整然とした図像に基づいているので、クエリを書くのも読むのも簡単です。

:::note info
名前は大文字と小文字を区別します。`:PERSON`、`:Person`、`:person`は異なる3つのラベルです。同様に`n`と`N`は異なる2つの変数です。
:::

### 構造

Cypherの構造はSQLから借用しています－クエリは様々な句（Clause）から構成されます。

句は互いに連結し、互いに中間結果のセットを生成します。例えば、ある`MATCH`句にマッチする変数が、次の句のコンテキストになります。

問い合わせ言語は、いくつかの句で構成されています。これらについては、[句](https://neo4j.com/docs/cypher-manual/current/clauses/)の章で詳しく説明します。

以下は、グラフから読み取るために使われる句の例です。

* `MATCH`：グラフパターンの問い合わせ。グラフからデータを取得する最も一般的な方法です。

* `WHERE`：それ自体の句ではなく、`MATCH`、`OPTIONAL MATCH`、`WITH`の一部です。パターンに制約を追加したり、`WITH`を通った中間結果をフィルタリングします。

* `RETURN`: どんな結果を戻すか。

`MATCH`と`RETURN`を実際に見てみましょう。

以下のクエリで簡単なグラフの例を作ってみましょう。
```Cypher:Cypher
CREATE (john:Person {name: 'John'})
CREATE (joe:Person {name: 'Joe'})
CREATE (steve:Person {name: 'Steve'})
CREATE (sara:Person {name: 'Sara'})
CREATE (maria:Person {name: 'Maria'})
CREATE (john)-[:FRIEND]->(joe)-[:FRIEND]->(steve)
CREATE (john)-[:FRIEND]->(sara)-[:FRIEND]->(maria)
```
![](https://kroki.io/graphviz/svg/eNqtj8EKgkAQhu89xWAHT0G60iXsVEFREnV0PWztlMK2K6sFYb57q0WUBHbwNMzsfjPfz5OTZmkMKyhAKo4QZjFL0dd4UJpDlt-EadRFcuTRuAcQDCE0BUCwPQrwwSo2qDMl75Kd0fTUXqpYUpuK0jIfoyczmEBAXuRBCaUrsu8i8cjIqqdHJfPfL-9L8-1iFkypbOx1Ot_r_JERvyM6tYrbuYrbqrLL8dqQIe0Q06zJVAG8zgN4rS5rppNPmfIBKVKtCA==)

例えば、次のクエリは、__'John'__ というユーザーと __'Johnの'__ 友達（しかし直接の友達ではない）を見つけ、__'John'__ と彼の見つかった友達の友達を戻すものです。

```Cypher:
MATCH (john {name: 'John'})-[:FRIEND]->()-[:FRIEND]->(fof)
RETURN john.name, fof.name
```

結果は以下です：
```
+----------------------+
| john.name | fof.name |
+----------------------+
| "John"    | "Maria"  |
| "John"    | "Steve"  |
+----------------------+
2 rows
```

次は、フィルタリングを追加してさらに多くの機能を動かしましょう。

ユーザー名のリストを取得し、このリストから名前を持つすべてのノードを見つけ、その友人とマッチし、__'name'__ プロパティが __'S'__ で始まる、フォローされたユーザーのみを戻します。

```Cypher:Cypher
MATCH (user)-[:FRIEND]->(follower)
WHERE user.name IN ['Joe', 'John', 'Sara', 'Maria', 'Steve'] AND follower.name =~ 'S.*'
RETURN user.name, follower.name
```

結果は以下です：
```
+---------------------------+
| user.name | follower.name |
+---------------------------+
| "John"    | "Sara"        |
| "Joe"     | "Steve"       |
+---------------------------+
2 rows
```

グラフを更新するための句の例は以下です。

* `CREATE` (および `DELETE`)：ノードとリレーションシップを作成（および削除）します。
* `SET`（および`REMOVE`）：`SET`はプロパティに値を設定し、ノードにラベルを追加します。`REMOVE`はそれらを削除します。
* `MERGE`：既存のノードやパターンを問い合わせたり、新しいノードを作成したりします。一意制約と一緒に使うと特に便利です。

# Neo4jのデータベースとグラフ

:::note info
本節では、Neo4jのデータベースとグラフについて説明します。
:::

CypherクエリはNeo4jデータベースに対して実行されますが、通常は特定のグラフに適用されます。これらの用語の意味と、グラフがデータベースでない場合を正確に理解することが重要です。

* __DBMS(DataBase Management System)__
Neo4jデータベース管理システムは、データベース内に複数のグラフを格納し管理することができます。クライアントアプリケーションは、DBMSに接続し、DBMSに対するセッションを開きます。クライアントセッションは、DBMS内の任意のグラフにアクセスできます。
* __グラフ__
グラフはデータベース内のデータモデルです。通常、各データベースには1つのグラフしかなく、特定のグラフを参照する[管理](https://neo4j.com/docs/cypher-manual/current/introduction/quering-updating-administering/)コマンドの多くは、データベース名を用いて行われます。<br>1つのセッションで実行されるCypherクエリは、どのグラフに適用するかを宣言するか、セッションで指定されたデフォルトを使用することができます。 
<br>Neo4j Fabricでは、同じクエリで複数のグラフを参照することが可能です。

* __データベース__
データベースは、ディスクやメモリ上の定義された空間において、データ収集のためのストレージであり、また検索のメカニズムです。

ほとんどのCypherのクエリは、グラフに対して実行される[読み込みや更新のクエリ](#問い合わせ、更新、管理)です。また、データベースやDBMS全体に適用される管理コマンドもあります。管理コマンドは、通常のユーザーデータベースに接続されたセッションで実行することはできず、_system_ データベースに接続されたセッション内で実行する必要があります。

## 1. システムデータベースとデフォルトデータベース
すべての Neo4j サーバーには`system`というデータベースが組み込まれており、他のデータベースとは異なる動作をします。`system`データベースはシステムデータを保存し、それに対してグラフクエリを実行することはできません。

Neo4jを新規にインストールすると2つのデータベースが含まれます：
* `system` - 前述のシステムデータベースで、DBMSとセキュリティ設定に関するメタデータを含みます。
* `neo4j` - デフォルトのデータベースで、設定オプション`dbms.default_database=neo4j`を使用して命名されます。

 _system_ データベースの詳細については、[データベース管理](https://neo4j.com/docs/cypher-manual/current/databases/)と[アクセス制御](https://neo4j.com/docs/cypher-manual/current/access-control/)を参照してください。

## 2. Neo4j のエディションの違い
Neo4jには、パフォーマンスや管理機能が追加された商用版のEnterprise Editionと、オープンソースのCommunity Editionの2つのエディションがあります。Cypherは、この2つのエディション間でほぼ同じように動作するため、このマニュアルの大部分では、この2つのエディションを区別していません。エディション間でCypherの言語サポートや動作に違いがある場合、[サポート機能の制限](##3.-サポート機能の制限)で説明するように、その点を強調します。
ただし、Community Editionでサポートされていない主要な部分を、下表にリストアップします。
| 機能 | Enterprise | Community |
|---|---|---|
| [複数のデータベース](https://neo4j.com/docs/cypher-manual/current/databases/)  |  任意の数のユーザーデータベース | `system`データベースと1つのユーザーデータベース  |
| ロールベースのセキュリティ  | ユーザー、ロール、権限の管理により、柔軟な[アクセス制御](https://neo4j.com/docs/cypher-manual/current/access-control/)と[サブグラフのアクセス制御](https://neo4j.com/docs/cypher-manual/current/access-control/manage-privileges/)が可能  | [マルチユーザー管理](https://neo4j.com/docs/cypher-manual/current/access-control/manage-users/)。すべてのユーザーがフルアクセス権を持つ  |
| 制約  | [存在制約](https://neo4j.com/docs/cypher-manual/current/constraints/examples/#administration-constraints-prop-exist-nodes)、[一意性制約](https://neo4j.com/docs/cypher-manual/current/constraints/examples/#administration-constraints-unique-nodes)、[ `NODE KEY`制約 ](https://neo4j.com/docs/cypher-manual/current/constraints/examples/#administration-constraints-node-key)  | [一意性制約](https://neo4j.com/docs/cypher-manual/current/constraints/examples/#administration-constraints-unique-nodes)のみ  |

## 3. サポート機能の制限
Cypherのいくつかの要素はNeo4jの全てのデプロイメントで動作しないので、特定のマーカーを使用してそのようなケースを強調します。
| マーカー | 説明 | 例 |
|---|---|---|
| `deprecated`  | この機能は非推奨で、将来のバージョンで削除される予定です。  | `DROP INDEX ON` `:Label(property)` <br> <font color=#d53f8c>**deprecated**</font>  |
| `enterprise-only`  | この機能はEnterpriseエディションでのみ使用できます。  | `CREATE DATABASE` `foo` <br> <font color=#38a169>**Enterprise Edition**</font>  |
| `fabric`  | この機能はFabricによるデプロイメントでのみ使用できます。  | `USE` `fabric.graph(0)` <br> <font color=#4299e1>**Fabric**</font> |

# 問い合わせ、更新、管理
:::note
本節では、グラフの問い合わせや更新、グラフやデータベースの管理について、Cypherの使用方法を説明します。
:::
[はじめに](#はじめに)では、Cypherを使用してグラフの読み取り専用のクエリを実行する一般的なケースを説明しました。しかし、Cypherを使用して、グラフの更新、グラフへのデータのインポート、グラフやデータベース、DBMS全体に対する管理操作を実行することも可能です。

これらの様々なオプションについては後で詳しく説明しますが、まずいくつかの重要なポイントをまとめておきましょう。

## 1. 管理クエリの構造
Cypherの管理クエリは、通常の読み書きクエリと組み合わせることはできません。各管理クエリは、`system`に対する更新動作か、`system`からのステータス情報の読み取りを実行します。管理コマンドの中には、特定のデータベースに変更を加えるものがあり、そのため、対象となるデータベースに接続されている場合にのみ実行可能です。他はDBMS全体の状態を変更するものであり、特殊な`system`データベースに対してのみ実行できます。
## 2. 更新クエリの構造
グラフから読み込んでグラフを更新する場合、クエリは暗黙のうちに2つのパートを持ちます - 読み込みが最初のパートで、書き込みが2番目のパートです。

:::note info
Cypherのクエリパートは、グラフを読み込んでマッチングを行うか、グラフの更新を行うかのどちらかであり、同時に両方は行われません。
:::
クエリが読み込みのみを行う場合、Cypherは結果が求められるまでパターンにマッチしません。更新クエリでは、解釈は、書き込みを行う前に __全ての__ 読み取りを行います。

クエリパーツが暗黙のうちに存在する唯一のパターンは、最初に読み込み、次に書き込みを行う場合です - それ以外の順序では、クエリパーツを明示的に記述する必要があります。クエリパーツは`WITH`文で分けられます。`WITH`は「事象の地平面」のようなもので、プランとそのプランの実行終了との間にあるバリアです。

集計されたデータを使ってフィルタリングを行う場合、2つの読み込みクエリパートを連結する必要があります。最初のクエリで集計し、2番目のクエリで集計された結果をフィルタリングします。
```Cypher:
MATCH (n {name: 'John'})-[:FRIEND]-(friend)
WITH n, count(friend) AS friendsCount
WHERE friendsCount > 3
RETURN n, friendsCount
```
`WITH`を使用すると、集計の方法を指定し、集計が終了してからCypherによるフィルタリングを開始するように指定できます。

以下は、グラフを更新し、集約されたデータをグラフに書き込む例です。
```Cypher:
MATCH (n {name: 'John'})-[:FRIEND]-(friend)
WITH n, count(friend) AS friendsCount
SET n.friendsCount = friendsCount
RETURN n.friendsCount
```
利用可能なメモリの許す限り、クエリを連結することができます。

## 3. データを戻す
どんなクエリでも、データを戻すことができます。もしクエリが読み込むだけなら、データを戻さなければなりません。もし、読み取りクエリがデータを戻さないなら、何の役にも立たないので有効なCypherクエリではありません。グラフを更新するクエリは何も戻す必要はありませんが、戻すことは可能です。

クエリの全てのパートの後に、最後の`RETURN`句があります。`RETURN`はどのクエリパートの一部でもなく、クエリの最後にあるピリオド記号です。`RETURN`句には、3つの副問い合わせが付属しています：`SKIP`/`LIMIT`と`ORDER BY`です。

もし、削除したばかりのクエリからノードやリレーションシップを戻すなら・・・注意してください、もはや有効ではないポインタを保持しているのです。

# トランザクション
:::note info
本節では、データベーストランザクションにおけるCypherクエリの動作を説明します。
:::
Cypherの全てのクエリは、トランザクション内で実行されます。クエリの更新によって行われた変更は、コミットされるまでトランザクションによってメモリ内に保持され、その時点で変更はディスクに保存され、他のトランザクションから見えるようになります。ゼロ除算などのクエリ評価時や制約違反などのコミット時にエラーが発生した場合、トランザクションは自動的にロールバックされ、グラフには何の変更も残されません。

つまり、更新クエリは常に完全に成功するか、全く成功しないかのどちらかです。

:::note info
大量の更新を行うクエリは、トランザクションがメモリに変更を保持するため、結果的に大量のメモリを使用することになります。Neo4jのメモリ設定については、[Neo4j運用マニュアル → メモリ構成](https://neo4j.com/docs/operations-manual/4.4/performance/memory-configuration/)を参照してください。
:::

トランザクションは __明示的なもの__ と __黙示的なもの__ があります。
* __明示的な__ トランザクション：
    * ユーザによって開かれます。
    * シーケンス中の複数のCypherクエリを実行します。
    * ユーザによってコミットされ、ロールバックされます。
* __黙示的な__ トランザクションは、自動コミットトランザクションや`:auto`トランザクションと呼ばれることもあります。
    * 自動で開かれます。
    * 1つのCypherクエリを実行します。
    * クエリが成功すると自動でコミットされます。

__[`CALL { …​ } IN TRANSACTIONS`](https://neo4j.com/docs/cypher-manual/current/clauses/call-subquery/#subquery-call-in-transactions)__ や __[`or PERIODIC COMMIT`](https://neo4j.com/docs/cypher-manual/current/query-tuning/using/#query-using-periodic-commit-hint)__ のような、別のトランザクションを開始するクエリは __黙示的な__ 実行のみ許可されます。

トランザクションの開始とコミットに使用されるAPIの例については、API固有のドキュメントを参照してください。
* Neo4j ドライバでのトランザクションの使用については、[Neo4jドライバマニュアル](https://neo4j.com/docs)中の _セッション API_ を参照してください。
* HTTP APIでのトランザクションの使用については、[HTTP APIのドキュメント → HTTP API の使用](https://neo4j.com/docs/http-api/4.4/actions/#http-api-actions)を参照してください。
* 組み込み型Core API内でのトランザクションの使用については、[Javaリファレンス → JavaからのCypherクエリの実行](https://neo4j.com/docs/java-reference/4.4/java-embedded/cypher-java/#cypher-java)を参照してください。

プロシージャを書くときやNeo4j embeddedを使うときは、実行結果から戻される全てのイテレータは、完全に使い果たすか閉じるべきであることを覚えておいてください。これにより、それらにバインドされているリソースが適切に解放されます。

## 1. DBMS トランザクション
DBMSに接続されているときにトランザクションを開始すると、DBMSレベルのトランザクションが開始されます。DBMSレベルのトランザクションは、データベーストランザクションのコンテナです。

データベーストランザクションは、特定のデータベースに対する最初のクエリが発行されたときに開始されます。DBMSレベルのトランザクションの中で開かれたデータベーストランザクションは、DBMSレベルのトランザクションがコミットまたはロールバックされるときにコミットまたはロールバックされます。

複数のデータベースへのクエリを1つのトランザクションで発行する例については、[Neo4jドライバマニュアル](https://neo4j.com/docs)の _Databases and execution context_ を参照してください。

DBMS トランザクションには、次のような制限があります。
* 1つのDBMSトランザクションで書き込めるデータベースは1つだけです。
* Cypherの操作は、主に以下のカテゴリに分類されます。これらの作業負荷を1つのDBMSトランザクションにまとめることはできません。
    * グラフの操作
    * スキーマコマンド
    * 管理コマンド

# Cypherパスマッチング
:::note info
Cypherのパスマッチングはリレーションシップの同型写像を利用しています。同じリレーションシップは同じ結果レコードに2回以上戻されることはありません。
:::
__Neo4j Cypher__ はパスマッチングに __リレーションシップの同型写像__ を利用しています。結果セットのサイズを縮小し、無限トラバースを防ぐ非常に効果的な方法です。

:::note info
Neo4jでは、全てのリレーションは方向を持っています。しかし、クエリ時には無向性のリレーションシップの概念を持つことができます。
:::
可変長のパターン式の場合、制約チェックは特に重要です。そうしないと無限の結果レコードが見つかる可能性があります。

理解を深めるために、いくつかの場合を考えてみましょう。

* __準同型写像__
パスマッチングに制約がない。

* __ノードの同型写像__
各パスマッチングレコードに対して、同じノードを2回以上戻すことはできない。

* __リレーションシップの同型写像__
各パスマッチングにおいて、同じ関係を複数回戻すことはできない。Cypherはパスマッチングにリレーションシップの同型写像を利用する。

## 1. 準同型写像
__制約：__ パスマッチングに制約はない。

例1. 準同型写像
グラフは2つのノード`(a)`と`(b)`のみからなり、`(a:Node)-[r:R]->(b:Node)`という1つのリレーションシップによって接続されています。
クエリが長さ`n`のパスを探していて、方向を気にしない場合、長さ`n`のパスは2つのノードを何度も往復したものが戻されます。

例えば、5つのリレーションシップを持ち、リレーションシップの方向を気にしない全てのパスを検索すると：
```Cypher:
MATCH p = ()-[*5]-()
RETURN nodes(p)
```
これは、準同型写像を利用した場合、`[a,b,a,b,a,b]`と`[b,a,b,a]`という2つの結果レコードを戻すことになります。
## 2. ノードの同型写像
__制約：__ 各パスマッチングレコードに対して、同じノードを複数回戻すことはできない。
`(a:Node)-[r:R]->(b:Node)`のような別の2ノードの例では、ノードの同型写像の制約により長さ1のパスしか見つけられません。

例2. ノードの同型写像
グラフは2つのノード`(a)`と`(b)`のみからなり、`(a:Node)-[r:R]->(b:Node)`という1つのリレーションシップによって接続されています。
```Cypher:
MATCH p = ()-[*1]-()
RETURN nodes(p)
```
これは、ノードの同型写像が使われた場合、`[a, b]`と`[b, a]`の2つの結果レコードを戻すことになります。

## 3. リレーションシップの同型写像
__制約：__ 各パスマッチングレコードに対して、同じリレーションシップを複数回戻すことはできない。
`(a:Node)-[r:R]->(b:Node)`のような別の2ノードの例では、リレーションシップの同型写像の制約により長さ1のパスしか見つけられません。

例3. リレーションシップの同型写像
グラフは2つのノード`(a)`と`(b)`のみからなり、`(a:Node)-[r:R]->(b:Node)`という1つのリレーションシップによって接続されています。
```Cypher:
MATCH p = ()-[*1]-()
RETURN nodes(p)
```
これは`[a, b]`と`[b, a]`の2つの結果レコードを戻します。

## 4. Cypherパスマッチの例
Cypherはパスマッチングにリレーションシップの同型写像を利用します。

例4. 友人の友人
あるユーザーの友人の友人を探しても、そのユーザーは戻ってこないはずです。

これを実証するために、いくつかのノードとリレーションシップを作成してみましょう。

__クエリ1. データの作成__
```Cypher:
CREATE
  (adam:User {name: 'Adam'}),
  (pernilla:User {name: 'Pernilla'}),
  (david:User {name: 'David'}),
  (adam)-[:FRIEND]->(pernilla),
  (pernilla)-[:FRIEND]->(david)
```
```Cypher:
Nodes created: 3
Relationships created: 2
Properties set: 3
```
この結果、次のようなグラフが得られます。
![](https://kroki.io/graphviz/svg/eNpLyUwvSizIUPBRqFbIy09JVYguzkgsSLUtSk3OL0pRKC6pzAFy8kvzUlJTYq25_AwUorkUFHISk1JzFGwVlKpDi1OLavISc1OBvBh1x5TE3Bj1mJxaJa5YLj9DvGoDUovyMnNyEhHqjfCqd0ksy0xBKDZQ0LVTgFqRnJ-TXwTSomyUamxibKYEFEvLzyvBJg433i3I09XPJSYP6lSQaUbUMa0WACgRYQY=)
では、Adamの友人の友人を探してみましょう。

__クエリ2. アダムの友人の友人__
```Cypher:
MATCH (user:User {name: 'Adam'})-[r1:FRIEND]-()-[r2:FRIEND]-(friend_of_a_friend)
RETURN friend_of_a_friend.name AS fofName
```
```Cypher:
+---------+
| fofName |
+---------+
| "David" |
+---------+

Rows: 1
```
このクエリでは、Cypherはパターンのリレーションシップ`r1`と`r2`が同じグラフのリレーションシップを指すマッチを戻さないようにしています。

しかし、これは必ずしも望ましいことではありません。もしクエリがユーザを戻すべきものであれば、次のように複数の`MATCH`句に渡ってマッチングを行うことが可能です：

__クエリ3. 複数のMATCH句__
```Cypher:
MATCH (user:User {name: 'Adam'})-[r1:FRIEND]-(friend)
MATCH (friend)-[r2:FRIEND]-(friend_of_a_friend)
RETURN friend_of_a_friend.name AS fofName
```
```Cypher:
+---------+
| fofName |
+---------+
| "David" |
| "Adam"  |
+---------+

Rows: 2
```
次の __クエリ4__ は、 __クエリ3__ と似ているように見えますが、実際には __クエリ2__ と等価であることに注意してください。

__クエリ4. クエリ2と等価__
```Cypher:
MATCH
  (user:User {name: 'Adam'})-[r1:FRIEND]-(friend),
  (friend)-[r2:FRIEND]-(friend_of_a_friend)
RETURN friend_of_a_friend.name AS fofName
```
```Cypher:
+---------+
| fofName |
+---------+
| "David" |
+---------+

Rows: 1
```