The Neo4j Cypher Manual v4.4の日本語訳です。正確な内容については必ず原文で確認を取るようにしてください。
また、順次訳しているため、リンク先が正しくないことがあります。
Neo4j Cypherマニュアルv4.4
🄫 2022 Neo4j, Inc.
ドキュメントのライセンスはCreative Commons 4.0です。
これは、Neo4jチームによって作成されたNeo4jバージョン4.4のためのCypherマニュアルです。
このマニュアルは、以下の領域をカバーしています。
- はじめに - Cypherクエリ言語の紹介
- 構文 - Cypherクエリの構文の学習
- 句 - Cypherクエリ句のリファレンス
- 関数 - Cypherクエリ関数のリファレンス
- 検索性能のためのインデックス - 検索性能のために使われるインデックスの管理法
- フルテキスト検索インデックス - フルテキスト検索を可能にする、フルテキストインデックスの使用法
- 制約 - データインテグリティを確保するために使用される制約の管理法
- データベース管理 - Neo4jデータベースを管理するためのCypherの使用法
- データベースエイリアス管理 - Neo4jにおけるデータベースエイリアスを管理するためのCypherの使用法
- アクセスコントロール - ロールベースのアクセス・コントロールときめ細かなセキュリティの管理法
- クエリのチューニング - クエリの分析と、性能のためにクエリをチューニングする方法の学習
- 実行プラン - Cypher実行の計画と演算子
- 非推奨、追加、互換性 - バージョン間の言語開発の概要
- キーワードの用語集 - Cypherのキーワードの用語集と、Cypherのマニュアルの他の部分へのリンク
- Cypherスタイルガイド - Cypherクエリを記述する際の推奨スタイルのガイド。
このマニュアルは、Neo4jクライアント・アプリケーションの開発者向けに書かれています。
はじめに
本章では、問い合わせ言語Cypherを紹介します。
Cypherとは?
Cypherは、グラフの問い合わせ、更新、管理を表現力豊かに効率的に行うことができる宣言型グラフ問い合わせ言語です。開発者と運用担当者の双方に適した設計になっています。Cypherはシンプルでありながら強力に設計されています。非常に複雑なデータベースクエリを簡単に表現できるため、データベースアクセスに迷うことなく、自分のドメインに集中することができます。
Cypherは、表現力豊かなクエリの慣習に基づく、多くの異なるアプローチやビルドから着想を得ています。WHEREやORDER BYといったキーワードの多くは、SQLからヒントを得ています。パターンマッチはSPARQLから表現のアプローチを借用しています。リストの意味合いはHaskellやPythonのような言語から借用したものです。Cypherの構造は英語の散文と整然とした図像に基づいているので、クエリを書くのも読むのも簡単です。
名前は大文字と小文字を区別します。:PERSON
、:Person
、:person
は異なる3つのラベルです。同様にn
とN
は異なる2つの変数です。
構造
Cypherの構造はSQLから借用しています-クエリは様々な句(Clause)から構成されます。
句は互いに連結し、互いに中間結果のセットを生成します。例えば、あるMATCH
句にマッチする変数が、次の句のコンテキストになります。
問い合わせ言語は、いくつかの句で構成されています。これらについては、句の章で詳しく説明します。
以下は、グラフから読み取るために使われる句の例です。
-
MATCH
:グラフパターンの問い合わせ。グラフからデータを取得する最も一般的な方法です。 -
WHERE
:それ自体の句ではなく、MATCH
、OPTIONAL MATCH
、WITH
の一部です。パターンに制約を追加したり、WITH
を通った中間結果をフィルタリングします。 -
RETURN
: どんな結果を戻すか。
MATCH
とRETURN
を実際に見てみましょう。
以下のクエリで簡単なグラフの例を作ってみましょう。
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)
例えば、次のクエリは、'John' というユーザーと 'Johnの' 友達(しかし直接の友達ではない)を見つけ、'John' と彼の見つかった友達の友達を戻すものです。
MATCH (john {name: 'John'})-[:FRIEND]->()-[:FRIEND]->(fof)
RETURN john.name, fof.name
結果は以下です:
+----------------------+
| john.name | fof.name |
+----------------------+
| "John" | "Maria" |
| "John" | "Steve" |
+----------------------+
2 rows
次は、フィルタリングを追加してさらに多くの機能を動かしましょう。
ユーザー名のリストを取得し、このリストから名前を持つすべてのノードを見つけ、その友人とマッチし、'name' プロパティが 'S' で始まる、フォローされたユーザーのみを戻します。
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のデータベースとグラフ
本節では、Neo4jのデータベースとグラフについて説明します。
CypherクエリはNeo4jデータベースに対して実行されますが、通常は特定のグラフに適用されます。これらの用語の意味と、グラフがデータベースでない場合を正確に理解することが重要です。
-
DBMS(DataBase Management System)
Neo4jデータベース管理システムは、データベース内に複数のグラフを格納し管理することができます。クライアントアプリケーションは、DBMSに接続し、DBMSに対するセッションを開きます。クライアントセッションは、DBMS内の任意のグラフにアクセスできます。 -
グラフ
グラフはデータベース内のデータモデルです。通常、各データベースには1つのグラフしかなく、特定のグラフを参照する管理コマンドの多くは、データベース名を用いて行われます。
1つのセッションで実行されるCypherクエリは、どのグラフに適用するかを宣言するか、セッションで指定されたデフォルトを使用することができます。
Neo4j Fabricでは、同じクエリで複数のグラフを参照することが可能です。 -
データベース
データベースは、ディスクやメモリ上の定義された空間において、データ収集のためのストレージであり、また検索のメカニズムです。
ほとんどのCypherのクエリは、グラフに対して実行される読み込みや更新のクエリです。また、データベースやDBMS全体に適用される管理コマンドもあります。管理コマンドは、通常のユーザーデータベースに接続されたセッションで実行することはできず、system データベースに接続されたセッション内で実行する必要があります。
1. システムデータベースとデフォルトデータベース
すべての Neo4j サーバーにはsystem
というデータベースが組み込まれており、他のデータベースとは異なる動作をします。system
データベースはシステムデータを保存し、それに対してグラフクエリを実行することはできません。
Neo4jを新規にインストールすると2つのデータベースが含まれます:
-
system
- 前述のシステムデータベースで、DBMSとセキュリティ設定に関するメタデータを含みます。 -
neo4j
- デフォルトのデータベースで、設定オプションdbms.default_database=neo4j
を使用して命名されます。
system データベースの詳細については、データベース管理とアクセス制御を参照してください。
2. Neo4j のエディションの違い
Neo4jには、パフォーマンスや管理機能が追加された商用版のEnterprise Editionと、オープンソースのCommunity Editionの2つのエディションがあります。Cypherは、この2つのエディション間でほぼ同じように動作するため、このマニュアルの大部分では、この2つのエディションを区別していません。エディション間でCypherの言語サポートや動作に違いがある場合、サポート機能の制限で説明するように、その点を強調します。
ただし、Community Editionでサポートされていない主要な部分を、下表にリストアップします。
機能 | Enterprise | Community |
---|---|---|
複数のデータベース | 任意の数のユーザーデータベース |
system データベースと1つのユーザーデータベース |
ロールベースのセキュリティ | ユーザー、ロール、権限の管理により、柔軟なアクセス制御とサブグラフのアクセス制御が可能 | マルチユーザー管理。すべてのユーザーがフルアクセス権を持つ |
制約 |
存在制約、一意性制約、 NODE KEY 制約
|
一意性制約のみ |
3. サポート機能の制限
Cypherのいくつかの要素はNeo4jの全てのデプロイメントで動作しないので、特定のマーカーを使用してそのようなケースを強調します。
マーカー | 説明 | 例 |
---|---|---|
deprecated |
この機能は非推奨で、将来のバージョンで削除される予定です。 |
DROP INDEX ON :Label(property) deprecated |
enterprise-only |
この機能はEnterpriseエディションでのみ使用できます。 |
CREATE DATABASE foo Enterprise Edition |
fabric |
この機能はFabricによるデプロイメントでのみ使用できます。 |
USE fabric.graph(0) Fabric |
問い合わせ、更新、管理
本節では、グラフの問い合わせや更新、グラフやデータベースの管理について、Cypherの使用方法を説明します。
はじめにでは、Cypherを使用してグラフの読み取り専用のクエリを実行する一般的なケースを説明しました。しかし、Cypherを使用して、グラフの更新、グラフへのデータのインポート、グラフやデータベース、DBMS全体に対する管理操作を実行することも可能です。
これらの様々なオプションについては後で詳しく説明しますが、まずいくつかの重要なポイントをまとめておきましょう。
1. 管理クエリの構造
Cypherの管理クエリは、通常の読み書きクエリと組み合わせることはできません。各管理クエリは、system
に対する更新動作か、system
からのステータス情報の読み取りを実行します。管理コマンドの中には、特定のデータベースに変更を加えるものがあり、そのため、対象となるデータベースに接続されている場合にのみ実行可能です。他はDBMS全体の状態を変更するものであり、特殊なsystem
データベースに対してのみ実行できます。
2. 更新クエリの構造
グラフから読み込んでグラフを更新する場合、クエリは暗黙のうちに2つのパートを持ちます - 読み込みが最初のパートで、書き込みが2番目のパートです。
Cypherのクエリパートは、グラフを読み込んでマッチングを行うか、グラフの更新を行うかのどちらかであり、同時に両方は行われません。
クエリが読み込みのみを行う場合、Cypherは結果が求められるまでパターンにマッチしません。更新クエリでは、解釈は、書き込みを行う前に 全ての 読み取りを行います。
クエリパーツが暗黙のうちに存在する唯一のパターンは、最初に読み込み、次に書き込みを行う場合です - それ以外の順序では、クエリパーツを明示的に記述する必要があります。クエリパーツはWITH
文で分けられます。WITH
は「事象の地平面」のようなもので、プランとそのプランの実行終了との間にあるバリアです。
集計されたデータを使ってフィルタリングを行う場合、2つの読み込みクエリパートを連結する必要があります。最初のクエリで集計し、2番目のクエリで集計された結果をフィルタリングします。
MATCH (n {name: 'John'})-[:FRIEND]-(friend)
WITH n, count(friend) AS friendsCount
WHERE friendsCount > 3
RETURN n, friendsCount
WITH
を使用すると、集計の方法を指定し、集計が終了してから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
です。
もし、削除したばかりのクエリからノードやリレーションシップを戻すなら・・・注意してください、もはや有効ではないポインタを保持しているのです。
トランザクション
本節では、データベーストランザクションにおけるCypherクエリの動作を説明します。
Cypherの全てのクエリは、トランザクション内で実行されます。クエリの更新によって行われた変更は、コミットされるまでトランザクションによってメモリ内に保持され、その時点で変更はディスクに保存され、他のトランザクションから見えるようになります。ゼロ除算などのクエリ評価時や制約違反などのコミット時にエラーが発生した場合、トランザクションは自動的にロールバックされ、グラフには何の変更も残されません。
つまり、更新クエリは常に完全に成功するか、全く成功しないかのどちらかです。
大量の更新を行うクエリは、トランザクションがメモリに変更を保持するため、結果的に大量のメモリを使用することになります。Neo4jのメモリ設定については、Neo4j運用マニュアル → メモリ構成を参照してください。
トランザクションは 明示的なもの と 黙示的なもの があります。
-
明示的な トランザクション:
- ユーザによって開かれます。
- シーケンス中の複数のCypherクエリを実行します。
- ユーザによってコミットされ、ロールバックされます。
-
黙示的な トランザクションは、自動コミットトランザクションや
:auto
トランザクションと呼ばれることもあります。- 自動で開かれます。
- 1つのCypherクエリを実行します。
- クエリが成功すると自動でコミットされます。
CALL { … } IN TRANSACTIONS
や or PERIODIC COMMIT
のような、別のトランザクションを開始するクエリは 黙示的な 実行のみ許可されます。
トランザクションの開始とコミットに使用されるAPIの例については、API固有のドキュメントを参照してください。
- Neo4j ドライバでのトランザクションの使用については、Neo4jドライバマニュアル中の セッション API を参照してください。
- HTTP APIでのトランザクションの使用については、HTTP APIのドキュメント → HTTP API の使用を参照してください。
- 組み込み型Core API内でのトランザクションの使用については、Javaリファレンス → JavaからのCypherクエリの実行を参照してください。
プロシージャを書くときやNeo4j embeddedを使うときは、実行結果から戻される全てのイテレータは、完全に使い果たすか閉じるべきであることを覚えておいてください。これにより、それらにバインドされているリソースが適切に解放されます。
1. DBMS トランザクション
DBMSに接続されているときにトランザクションを開始すると、DBMSレベルのトランザクションが開始されます。DBMSレベルのトランザクションは、データベーストランザクションのコンテナです。
データベーストランザクションは、特定のデータベースに対する最初のクエリが発行されたときに開始されます。DBMSレベルのトランザクションの中で開かれたデータベーストランザクションは、DBMSレベルのトランザクションがコミットまたはロールバックされるときにコミットまたはロールバックされます。
複数のデータベースへのクエリを1つのトランザクションで発行する例については、Neo4jドライバマニュアルの Databases and execution context を参照してください。
DBMS トランザクションには、次のような制限があります。
- 1つのDBMSトランザクションで書き込めるデータベースは1つだけです。
- Cypherの操作は、主に以下のカテゴリに分類されます。これらの作業負荷を1つのDBMSトランザクションにまとめることはできません。
- グラフの操作
- スキーマコマンド
- 管理コマンド
Cypherパスマッチング
Cypherのパスマッチングはリレーションシップの同型写像を利用しています。同じリレーションシップは同じ結果レコードに2回以上戻されることはありません。
Neo4j Cypher はパスマッチングに リレーションシップの同型写像 を利用しています。結果セットのサイズを縮小し、無限トラバースを防ぐ非常に効果的な方法です。
Neo4jでは、全てのリレーションは方向を持っています。しかし、クエリ時には無向性のリレーションシップの概念を持つことができます。
可変長のパターン式の場合、制約チェックは特に重要です。そうしないと無限の結果レコードが見つかる可能性があります。
理解を深めるために、いくつかの場合を考えてみましょう。
-
準同型写像
パスマッチングに制約がない。 -
ノードの同型写像
各パスマッチングレコードに対して、同じノードを2回以上戻すことはできない。 -
リレーションシップの同型写像
各パスマッチングにおいて、同じ関係を複数回戻すことはできない。Cypherはパスマッチングにリレーションシップの同型写像を利用する。
1. 準同型写像
制約: パスマッチングに制約はない。
例1. 準同型写像
グラフは2つのノード(a)
と(b)
のみからなり、(a:Node)-[r:R]->(b:Node)
という1つのリレーションシップによって接続されています。
クエリが長さn
のパスを探していて、方向を気にしない場合、長さn
のパスは2つのノードを何度も往復したものが戻されます。
例えば、5つのリレーションシップを持ち、リレーションシップの方向を気にしない全てのパスを検索すると:
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つのリレーションシップによって接続されています。
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つのリレーションシップによって接続されています。
MATCH p = ()-[*1]-()
RETURN nodes(p)
これは[a, b]
と[b, a]
の2つの結果レコードを戻します。
4. Cypherパスマッチの例
Cypherはパスマッチングにリレーションシップの同型写像を利用します。
例4. 友人の友人
あるユーザーの友人の友人を探しても、そのユーザーは戻ってこないはずです。
これを実証するために、いくつかのノードとリレーションシップを作成してみましょう。
クエリ1. データの作成
CREATE
(adam:User {name: 'Adam'}),
(pernilla:User {name: 'Pernilla'}),
(david:User {name: 'David'}),
(adam)-[:FRIEND]->(pernilla),
(pernilla)-[:FRIEND]->(david)
Nodes created: 3
Relationships created: 2
Properties set: 3
この結果、次のようなグラフが得られます。
では、Adamの友人の友人を探してみましょう。
クエリ2. アダムの友人の友人
MATCH (user:User {name: 'Adam'})-[r1:FRIEND]-()-[r2:FRIEND]-(friend_of_a_friend)
RETURN friend_of_a_friend.name AS fofName
+---------+
| fofName |
+---------+
| "David" |
+---------+
Rows: 1
このクエリでは、Cypherはパターンのリレーションシップr1
とr2
が同じグラフのリレーションシップを指すマッチを戻さないようにしています。
しかし、これは必ずしも望ましいことではありません。もしクエリがユーザを戻すべきものであれば、次のように複数のMATCH
句に渡ってマッチングを行うことが可能です:
クエリ3. 複数のMATCH句
MATCH (user:User {name: 'Adam'})-[r1:FRIEND]-(friend)
MATCH (friend)-[r2:FRIEND]-(friend_of_a_friend)
RETURN friend_of_a_friend.name AS fofName
+---------+
| fofName |
+---------+
| "David" |
| "Adam" |
+---------+
Rows: 2
次の クエリ4 は、 クエリ3 と似ているように見えますが、実際には クエリ2 と等価であることに注意してください。
クエリ4. クエリ2と等価
MATCH
(user:User {name: 'Adam'})-[r1:FRIEND]-(friend),
(friend)-[r2:FRIEND]-(friend_of_a_friend)
RETURN friend_of_a_friend.name AS fofName
+---------+
| fofName |
+---------+
| "David" |
+---------+
Rows: 1