Skip to content

Instantly share code, notes, and snippets.

@ykst
Created December 19, 2018 16:48
Show Gist options
  • Save ykst/60ecf2b21fc1ed937ec01af163fdf6a0 to your computer and use it in GitHub Desktop.
Save ykst/60ecf2b21fc1ed937ec01af163fdf6a0 to your computer and use it in GitHub Desktop.
逆引きCypher

逆引きCypher

特に断りがない限り、環境はOSX、neo4jは3.4.5を参照。

メンテナンス系

ユーザーパスワードの変更

REST APIの/user/<user name>/passwordに対してPOSTする。

curl -H "Content-Type: application/json" -XPOST -d '{"password":"new_password"}' -u your_name:old_password http://localhost:7474/user/your_name/password

CALLで呼べる関数を調べる

CALL dbms.procedures()

CLIからプラグインを追加する

neo4jがインストールされている場所のpluginsディレクトリにjarファイルを配置してneo4jサーバーをリスタートする。 brewからインストールした場合は/usr/local/Cellar/neo4j/3.4.5/libexec/pluginsが対象ディレクトリ。

サーバーの再起動は以下。

$ neo4j restart

更新系

既存のノード間にリレーションを作成する

先にノードをMATCHしてから、参照を使ってリレーションをCREATEする。

MATCH (m:A),(n:B) CREATE (m)-[:R]->(n);

次のようにノードの定義をCREATE文に含めると、リレーションと一緒にノードも作成されてしまう。

CREATE (m:A)-[:R]->(n:B);

既存ノードの属性を変更する

SETを使用する。

MATCH (n:X) SET n.x = 1 RETURN n;

既存ノードのプロパティをコピーして新規ノードを作成する

SETでノードを代入するとプロパティのコピーが行われる。ラベルのコピーまでは行われない点に注意。:

MATCH (n:Foobar) CREATE (copy:Foobar) SET copy = n;

ラベルを消去する

REMOVEを使用する

MATCH (n) REMOVE n:Foobar;

全てのプロパティを削除する

SETで空オブジェクトを代入するとプロパティが全て削除される。

MATCH (n) SET n = {};

全消去

MATCH (n) OPTIONAL MATCH (n)-[r]-() DELETE n,r;

リレーションだけ全消去する

MATCH ()-[e]-() DELETE e;

参照系

ラベルを無視して、プロパティだけを使って検索をする

WHERE節を使用する。

MATCH (n) WHERE n.prop = "foobar" RETURN n;

ただし、対象プロパティにインデックスを張っておかないと検索が非効率なフルノードスキャンになってしまうのに注意。

向きやラベル、属性に関係なく、直接リレーションで接続しているノードを抽出する

MATCH (m)--(n) RETURN m, n;

ちなみに、(a)--(b)(a)-[]-(b)は同じ意味になる。

特定のラベルを持つリレーションで連結しているサブグラフを抽出する

MATCH (m)-[:A]-(n) RETURN m, n;

二点の最短経路をサブグラフとして抽出する

MATCH p=shortestPath((:A)-[*]-(:B)) RETURN p;

実用上は、検索対象が広がり過ぎないように最大長さを指定しておく。

MATCH p=shortestPath((:A)-[*..5]-(:B)) RETURN p;

既知の候補に含まれるプロパティを持っているかどうかで検索する

INを使用する。

MATCH (user:User) WHERE user.name IN ['Joe', 'John', 'Sara', 'Maria', 'Steve'] RETURN user;

正規表現でプロパティを検索する

=~を使用する。対象文字列の全体にマッチさせる必要がある点に注意。

MATCH (user:User) WHERE user.name =~ 'St.*' RETURN user;

特定の正規表現にマッチしないことを条件にする場合はNOTを使用する。

MATCH (user:User) WHERE NOT (user.name =~ 'St.*') RETURN user;

プロパティが定義されているかという条件で検索する

existsを使用する。

MATCH (n) WHERE exists(n.foobar) RETURN n;

マッチした全てのノードに対して成立する条件で検索する

nodesでマッチしたサブグラフのノード集合を取得し、ALLで全てのノードに必要な条件を記述する。

MATCH p =(a)-[*1..3]->(b)
WHERE a.name = 'Alice' AND b.name = 'Daniel' AND ALL (x IN nodes(p) WHERE x.age > 30)
RETURN p;

マッチした全てのリレーションに対して成立する条件で検索する

relationshipsでマッチしたサブグラフのリレーション集合を取得し、ALLで全てのに必要な条件を記述する。

MATCH (ms:Person { name: 'Martin Sheen' }),(cs:Person { name: 'Charlie Sheen' }), p = shortestPath((ms)-[:ACTED_IN*]-(cs))
WHERE ALL (r IN relationships(p) WHERE exists(r.role))
RETURN p;

複数種類のラベルのいずれかにマッチするノードを検索する

UNIONを使用して複数のMATCHした結果を合成する。

MATCH (n:Male)
RETURN n
UNION MATCH (n:Female)
RETURN n;

以下のようにOR条件で簡潔に検索することも出来る。

MATCH (n) WHERE n:Male OR n:Female RETURN n;

通常はこちらの方法で問題ない。ただし、古いバージョンのneo4jではノードのフルスキャンが発生する可能性がある。実際にどうなるかPROFILEでプランを確認しておくとよい。

PROFILE MATCH (n) WHERE n:Male OR n:Female RETURN n;

https://stackoverflow.com/questions/20003769/neo4j-match-multiple-labels-2-or-more

特定のノードから到達可能なサブグラフを検索する

ソースノードに対してクエリを行い、-[*]->で任意の長さのパスを指定する。

MATCH (n)-[*]->(m) WHERE n.id = "foobar" RETURN n, m;

ただし、グラフの規模によっては高負荷になる可能性があるため、なるべく深さを制限するようなクエリにした方がよい。

MATCH (n)-[*..3]->(m) WHERE n.id = "foobar" RETURN n, m;

ノード数を調べる

count(*)はマッチしたノードを数え上げる。

MATCH (n) RETURN count(*);

特定のノードから到達可能なノード数を調べる

-[*]->は接続を長さ別に全て数え上げてパスを返すので、DISTINCTで重複を排除してCOUNTする必要がある。

MATCH (n)-[*]->(m) WHERE n.id = "foobar" RETURN COUNT(DISTINCT m);

nodesでパスからノードを抽出する方法も使える。

MATCH p = (n)-[*]->(m) WHERE n.id = "foobar" RETURN COUNT(nodes(p));

使用されている全てのノードラベルを調べる

ラベルを集約する。

MATCH (n)
RETURN DISTINCT labels(n);

ただしこれは、正確には「ノードに割り当てられている全ての複合ラベルの組み合わせ」となる。単体のラベルに分けて一覧したい場合は、db.labels()を使用する。

CALL db.labels();

ちなみにbrowserのDatabase Informationタブにも一覧が表示されている。

使用されている全てのリレーションタイプを調べる

リレーションには一つのタイプしか割り当てられないため、集約で全種類が調べられる。

MATCH ()-[r]-()
RETURN DISTINCT type(r);

または、

CALL db.relationshipTypes();

接続先の無いノードを検索する

WHEREでパスを条件にして絞り込む。

MATCH (n) WHERE NOT (n)-->() RETURN n;

特定の長さのパスを検索する

長さが丁度3つ

MATCH p = ()-[:*3]->() RETURN p;

3つ以上の場合

MATCH p = ()-[:*3..]->() RETURN p;

3つ以下の場合

MATCH p = ()-[:*..3]->() RETURN p;

3つ以上5つ以下の場合

MATCH p = ()-[:*3..5]->() RETURN p;

マッチしたパスを単体のノード+リレーションに分解する

パスではなく、(a)-[r]->(b)の形で一行ずつ結果を処理したい場合は、最初にパスをマッチしてからサブクエリで1対1の関係をマッチさせる。

MATCH p = (n)-[*]->(m) WHERE n.id = 'Start' WITH DISTINCT p, m AS b MATCH (a)-[r]->(b) WHERE a in nodes(p) RETURN a,r,b;

複数のタイプのうちいずれかにマッチするリレーションを検索する

|でタイプを区切る。

MATCH p = (m)-[:A|B*]->(n) RETURN p;

それぞれのタイプの共通プロパティであれば検索条件に含めることが出来る。

MATCH p = (m)-[:A|B* { id: "foo" }]->(n) RETURN p;

最適化

プロパティにインデックスを作成する

ラベルとプロパティを指定してインデックスを作成する。RDBMSと同様に、書き込み速度とディスクスペースを犠牲にして、そのプロパティを使った検索を高速化させる。

CREATE INDEX ON :Foobar(id);

複合インデックスはカンマ区切りで指定する。

CREATE INDEX ON :Foobar(id, name);

インデックスはバックグラウンドで作成されるため、直後ではまだ有効になっていない場合がある。

定義されているインデックスを調べる

インデックスの作成ステートもこれで調べられる。

CALL db.indexes()

インデックスを削除する

ラベルとプロパティ名を指定して削除する。

DROP INDEX ON :Foobar(id);

プロパティにユニーク制約を加える

CREATE CONSTRAINT ON (n:Node) ASSERT n.id IS UNIQUE;

複合キーのユニーク制約はenterpriseバージョンのNODE KEY機能が必要。

制約を削除するには同じ条件でDROP CONSTRAINTを使用する。

DROP CONSTRAINT ON (n:Node) ASSERT n.id IS UNIQUE;

全てのラベルの共通プロパティにインデックスを作成する

インデックスはラベルを通して働くため、無条件で全てのノードにインデックスを張ることは出来ない。そこで、インデックス用のラベルを全てのノードに共通で持たせて、そのプロパティを対象賭する。

MATCH (n) SET n:Index
CREATE INDEX ON Index(id);

ここで作ったラベルを検索時に含めないと、インデックスが働かない点に注意する必要がある。PROFILEを使ってクエリにインデックスが効いているかどうかを確認するとよい。

全てのインデックスを消去する

ネイティブなクエリではサポートされていないので、APOCを使う。

CALL apoc.schema.assert({}, {})

または、REST APIでラベルとプロパティを取得して、スクリプトでDROPクエリを発行する。 https://stackoverflow.com/a/26077913

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment