Neo4jを使ってみたよ。とりあえず触ってみたかったのでインストールはせずにGraphGistを使ってCypherの書き方を勉強してみた。

Cypherって?

グラフデータベースを操作するためのSQLのこと。他のグラフデータベースに利用されているのかは知らないけれどNeo4jに関してはこのCypherを使ってデータを操作する。SQLを知らないとMySQLに対して何もできないのと同じようにCypherを知らないとNeo4jを何もできない。今回はこのCypherの基本的な構文や書き方を書いてみる。

本家のドキュメントはここだよ

ノードの作成

CREATE (n:Actor {name:"Doraemon"})

Actorは型。nameは属性。nはマッチさせるときの記述子

エッジの作成

MATCH (actor:Actor)
WHERE actor.name = "Doraemon"
CREATE (n:Actor {name:"Nobita"})
CREATE (actor)-[:HELP]->(n)

Actor型の中でnameがDoraemonのものを探す。見つけたらNobitaを作成し、その間にHELP型のエッジを作成する。

UNIQUEで作成する

エッジを同じものを複製したくなければCREATE UNIQUEを使う

MATCH (actor:Actor)
WHERE actor.name = "Doraemon"
CREATE UNIQUE (actor)-[r:ACTED_IN]->(movie:Movie {title:"Mugen Sankenshi"})
RETURN r;

属性の変更

MATCH (actor:Actor)
WHERE actor.name = "Doraemon"
SET actor.birth_year = 2020
RETURN actor.name, actor.birth_year;

全てのノードの列挙

MATCH (actor:Actor)
RETURN actor as `All Actors`;

パターンマッチ

グラフデータベースの検索は基本的にパターンマッチを使って行う。既にいくつか出てきたけれどパターンマッチにはMATCH文を使う。

エッジのマッチ

(a)-[r]->(b)

ノードは丸括弧で囲み、エッジは角括弧で囲む。

(a)-[r:HELP]->(b)

型にもマッチさせたいときに型も書く。 無向グラフであれば矢印にはしない.

(a)--(b)

複数のタイプにorでマッチさせたいときには縦棒を使う

(a)-[r:TYPE1|TYPE2]->(b)

Optional relationship

マッチしない場合にはその要素だけnullを返して欲しい場合には?を使う。

START me = node(*)
MATCH (me)-->(friend)-[?]->(friend_of_friend)
RETURN friend,friend_of_friend

この場合にはこの関係にマッチしないノードがあった場合にはそこの欄だけnullになる。関係があるかないかまだわからないけれど、とりあえずすべての見てみたい場合に有効。

エッジの個数を指定したい場合は*を使う

START me = node(4)
MATCH (me)-[?*2]->(friend)
RETURN friend

自分から2つ関係をまたいだ友達だけ返す。範囲指定も可能。

START me = node(4)
MATCH (me)-[?*2..4]->(friend)
RETURN friend

2から4までの関係をまたいだ友達。

パスの割り当て

マッチしたパスを見たい場合があるときは、そのまま代入させてしまえばいい。

START me = node(3)
MATCH p1 = (me)-[*2]-(friendOfFriend)
CREATE p2 = (me)-[:MARRIED_TO]->(wife {name:"Gunhild"})
CREATE UNIQUE p3 = wife-[:KNOWS]-friendOfFriend
RETURN p1,p2,p3

参照構文

要素の検索、参照に必要な4つの構文(START, MATCH, WHERE, RETURN)に関して

START

STARTは探索を行うためのアンカーとなるノード、またはエッジを決める構文。

以下のようなグラフがあったときに

START n = node(1)

と書くとnにnodeの1がバインドされ、これを使って探索することが可能。

Graphの例

複数ノード、または全てのノードに対して処理を行いたい場合はこんな感じ。

START n = node(1,2,3)
RETURN n
START n = node(*)
RETURN n

アンカーとなるエッジをバインドさせたい場合は以下のように書く。

START r = relationship(0)
RETURN r

複数の点から始めるにはこんな感じ。(このやり方が知りたかった)

START a = node(1), b = node(2)
RETURN a,b

MATCH

nodeやエッジにパターンマッチさせるための構文。 STARTなしでもMATCHは使えることはさっき書いたけれど、この場合でのどちらにしろCypherはすべてのノードを探索して始点となるノードを探すことになる。

すべてのノードを得たい場合は簡単。CypherはSTARTを指定しなければすべてのノードを探索するので、条件がなければすべてのノードにマッチする。

MATCH n
RETURN n

ラベルにマッチさせたいときはコロンで区切る

MATCH n:Actor
RETURN n

エッジもマッチに含めてよい

MATCH (director)-->(movie)
WHERE director.name = "Oliver Stone"
RETURN movie.title

エッジにラベルをマッチさせてもよい

MATCH (movie)<-[r:ACTED_IN]-(actor)
WHERE actor.name = "Tom Hanks"
RETURN movie.title

エッジの数でマッチさせることもできる

MATCH (martin)-[:ACTED_IN*1..2]-(x)
WHERE martin.name = "Martin Sheen"
RETURN x

WHERE

MATCHで引っかかったものをfilterにかける処理を行う文

MATCH n
WHERE n.name = "Takeshi" XOR (n.age < 30 AND n.name = "Nobita") OR NOT (n.name = "Suneo" OR n.name = "Dekisugi")
RETURN n

名前がたけしである場合か、30際以下ののび太である場合かスネオでも出来杉でもない場合のノードが返ってくる MATCHでラベルを使うこともできたが、WHEREでも可能

MATCH n
WHERE n:Actor
RETURN n

Actorだけが返ってくる。また正規表現も使える

MATCH n
WHERE n.name =~ "T.*"
RETURN n

多分”Takeshi”が返ってくる。ある属性があるかないかで取る場合はHASを使う

MATCH n
WHERE HAS(n.belt)
RETURN n

belt属性があるものが得られる。

RETURN

返り値を選択するのがRETURN文だけれどここで数を返した、平均を返したりといったことができる。

MATCH (n)-->(x)
WHERE n.name = "X"
RETURN n, count(*)

MATCHした数が返ってくる。

MATCH (n)-[r]->(x)
WHERE n.name = "Nobita"
RETURN type(r)

エッジのラベルが返ってくる。KNOWSとか。 SQLと同じように他にもSUMとかAVGとか使えるみたい。

大体基本的な構文はわかってきたので、次はGraphGistを使ってもう少し複雑なものを作ってみようかな。