仕事でグラフデータベースが利用される案件が目につくようになってきました。
というわけで。Neo4j に手を出してみた顛末の記録です。
ちなみに。プラットフォームは OS X El Capitan です。
Neo4j のインストール
横着して Homebrew でインストールしました。
$ brew install neo4j ==> Downloading http://dist.neo4j.org/neo4j-community-2.2.5-unix.tar.gz ######################################################################## 100.0%t 🍺 /usr/local/Cellar/neo4j/2.2.5: 92 files, 60M, built in 77 seconds $ which neo4j /usr/local/bin/neo4j
無事インストールできました。
サーバを起動する
$ neo4j start Starting Neo4j Server...WARNING: not changing user process [87422]... waiting for server to be ready..... OK. http://localhost:7474/ is ready.
ブラウザで http://localhost:7474/ にアクセスすると、最初にログインが求められます。
表示されたページにパスワードの初期値が書かれていますのでそれでログインします。ログインしたらパスワード変更ページが表示されるのでパスワードを変更します。
これでウェブ管理ツールが使えるようになりました。
…なのですが。ここから先はウェブ管理ツールは使わずコマンドラインツールの neo4j-shell を使っていきます。
起動する。
$ neo4j-shell Welcome to the Neo4j Shell! Enter 'help' for a list of commands NOTE: Remote Neo4j graph database service 'shell' at port 1337 neo4j-sh (?)$
起動しました。
CRUD
Neo4j には Cypher というクエリ言語が用意されています。Neo4j Shell 上で Cypher を書いて CRUD を試みていきます。
ノードを作る
Neo4j のデータはノード (nodes) と関係 (relationships) から作られています。グラフ理論で言うところのノードとエッジです。Neo4j のグラフは有向グラフなので関係には方向があります。
まずノードの作成です。
$ CREATE (n:Writer {name: 'Eric'}); +-------------------+ | No data returned. | +-------------------+ Nodes created: 1 Properties set: 1 Labels added: 1 24 ms
これでプロパティとして name
(値は Eric
)を持ち Writer
というラベルがついたノードが作成できました。
が、値が返されていないので本当に作られているのかよくわかりません。
RETURN 節をつけて値を返すようにしてみます。
$ CREATE (n:Writer {name: 'Jim'}) RETURN n; +----------------------+ | n | +----------------------+ | Node[12]{name:"Jim"} | +----------------------+ 1 row Nodes created: 1 Properties set: 1 Labels added: 1 34 ms
RETURN n
としたことで n
の値、つまり作成されたノードの値が表示されています。
別のラベルをつけたノードを作成してみます。
$ CREATE (n:Book {name: 'Seven Databases in Seven Weeks'}) RETURN n; +-------------------------------------------------+ | n | +-------------------------------------------------+ | Node[13]{name:"Seven Databases in Seven Weeks"} | +-------------------------------------------------+ 1 row Nodes created: 1 Properties set: 1 Labels added: 1 44 ms
ノードを取得する
ラベル Writer
がついたノードを取得してみます。
$ MATCH (n:Writer) RETURN n; +-----------------------+ | n | +-----------------------+ | Node[11]{name:"Eric"} | | Node[12]{name:"Jim"} | +-----------------------+ 2 rows 59 ms
先ほど登録した2つのノードが表示されました。Node の後に表示されている数字は id ですのでデータベースの状態によって異なります。
次に name
の値が Eric
のノードを取得します。
$ MATCH (n {name: 'Eric'}) RETURN n; +-----------------------+ | n | +-----------------------+ | Node[11]{name:"Eric"} | +-----------------------+ 1 row 58 ms
この例ではノードのラベルを指定していません。このため他に {name: 'Eric'}
というノードがあったばあいはラベルに関係なく取得されます。
WHERE 節を使ってもプロパティを指定することができます。
$ MATCH (n) WHERE n.name = 'Eric' RETURN n; +-----------------------+ | n | +-----------------------+ | Node[11]{name:"Eric"} | +-----------------------+ 1 row 52 ms
SQL と同じように WHERE 節は色々な条件を指定することができます。
条件を何も指定しなければ、すべてのノードを取得することができます。
$ MATCH (n) RETURN n; +-------------------------------------------------+ | n | +-------------------------------------------------+ | Node[11]{name:"Eric"} | | Node[12]{name:"Jim"} | | Node[13]{name:"Seven Databases in Seven Weeks"} | +-------------------------------------------------+ 3 rows 29 ms
ノードのラベルを知りたいばあいは labels 関数が使えます。
$ MATCH (n) RETURN n, labels(n); +--------------------------------------------------------------+ | n | labels(n) | +--------------------------------------------------------------+ | Node[11]{name:"Eric"} | ["Writer"] | | Node[12]{name:"Jim"} | ["Writer"] | | Node[13]{name:"Seven Databases in Seven Weeks"} | ["Book"] | +--------------------------------------------------------------+ 3 rows 96 ms
関係を作る
既存のノードに関係を作るばあい、MATCH で該当するノードを取得し、それに対して CREATE で関係を作成します。
$ MATCH (w:Writer), (b:Book) CREATE (w)-[r:Write]->(b) RETURN *; +---------------------------------------------------------------------------------------+ | b | r | w | +---------------------------------------------------------------------------------------+ | Node[13]{name:"Seven Databases in Seven Weeks"} | :Write[0]{} | Node[11]{name:"Eric"} | | Node[13]{name:"Seven Databases in Seven Weeks"} | :Write[1]{} | Node[12]{name:"Jim"} | +---------------------------------------------------------------------------------------+ 2 rows Relationships created: 2 89 ms
Writer
とラベルがついたノードと Book
とラベルがついたノードに Write
とラベルがついた関係が作成されました。
ここでは RETURN 節で *
を指定しているのですべての変数の値を表示しています。
関係は有向なので、逆向きの WrittenBy
という関係を作成してみます。
$ MATCH (w:Writer), (b:Book) CREATE (w)<-[r:WrittenBy]-(b) RETURN *; +-------------------------------------------------------------------------------------------+ | b | r | w | +-------------------------------------------------------------------------------------------+ | Node[13]{name:"Seven Databases in Seven Weeks"} | :WrittenBy[4]{} | Node[11]{name:"Eric"} | | Node[13]{name:"Seven Databases in Seven Weeks"} | :WrittenBy[5]{} | Node[12]{name:"Jim"} | +-------------------------------------------------------------------------------------------+ 2 rows Relationships created: 2 34 ms
このように関係の向きは逆向きでも記述することができます。
すべてのノードと関係を表示してみます。
$ MATCH (n1)-[r]->(n2) RETURN n1, r, n2; +---------------------------------------------------------------------------------------------------------------------+ | n1 | r | n2 | +---------------------------------------------------------------------------------------------------------------------+ | Node[13]{name:"Seven Databases in Seven Weeks"} | :WrittenBy[4]{} | Node[11]{name:"Eric"} | | Node[13]{name:"Seven Databases in Seven Weeks"} | :WrittenBy[5]{} | Node[12]{name:"Jim"} | | Node[12]{name:"Jim"} | :Write[1]{} | Node[13]{name:"Seven Databases in Seven Weeks"} | | Node[11]{name:"Eric"} | :Write[0]{} | Node[13]{name:"Seven Databases in Seven Weeks"} | +---------------------------------------------------------------------------------------------------------------------+ 4 rows 36 ms
関係は向きを省略して指定することもできます。
$ MATCH (n1)-[r]-(n2) RETURN DISTINCT n1, r, n2; +---------------------------------------------------------------------------------------------------------------------+ | n1 | r | n2 | +---------------------------------------------------------------------------------------------------------------------+ | Node[13]{name:"Seven Databases in Seven Weeks"} | :Write[0]{} | Node[11]{name:"Eric"} | | Node[13]{name:"Seven Databases in Seven Weeks"} | :WrittenBy[4]{} | Node[11]{name:"Eric"} | | Node[13]{name:"Seven Databases in Seven Weeks"} | :Write[1]{} | Node[12]{name:"Jim"} | | Node[13]{name:"Seven Databases in Seven Weeks"} | :WrittenBy[5]{} | Node[12]{name:"Jim"} | | Node[12]{name:"Jim"} | :Write[1]{} | Node[13]{name:"Seven Databases in Seven Weeks"} | | Node[11]{name:"Eric"} | :Write[0]{} | Node[13]{name:"Seven Databases in Seven Weeks"} | | Node[12]{name:"Jim"} | :WrittenBy[5]{} | Node[13]{name:"Seven Databases in Seven Weeks"} | | Node[11]{name:"Eric"} | :WrittenBy[4]{} | Node[13]{name:"Seven Databases in Seven Weeks"} | +---------------------------------------------------------------------------------------------------------------------+ 8 rows 45 ms
向きを省略したので、同じ意味のものが (n1)-[r]->(n2)
と (n1)<-[r]-(n2)
のそれぞれで取得されています。
関係をノードと同時作成する場合は、ノードを作成する CREATE と一緒に記述することができます。
$ CREATE (e:Writer {name: 'Eric'}), (j:Writer {name: 'Jim'}), (s:Book {name: 'Seven Databases in Seven Weeks'}), (e)-[:Write]->(s), (j)-[:Write]->(s), (s)-[:WrittenBy]->(e), (s)-[:WrittenBy]->(j) RETURN *; +------------------------------------------------------------------------------------------------+ | e | j | s | +------------------------------------------------------------------------------------------------+ | Node[14]{name:"Eric"} | Node[15]{name:"Jim"} | Node[16]{name:"Seven Databases in Seven Weeks"} | +------------------------------------------------------------------------------------------------+ 1 row Nodes created: 3 Relationships created: 4 Properties set: 3 Labels added: 3 143 ms
関係にもプロパティを設定することができます。
ここではもう一つ Book
ラベルのついたノードを作成し、同時にラベルが Translate
でプロパティとして {language: 'Japanese'}
を設定した関係を作成しています。
$ MATCH (e:Book {name: 'Seven Databases in Seven Weeks'}) CREATE (j:Book {name: '7つのデータベース 7つの世界'}), (e)-[r:Translate {language: 'Japanese'}]->(j) RETURN *; +--------------------------------------------------------------------------------------------------------------------------+ | e | j | r | +--------------------------------------------------------------------------------------------------------------------------+ | Node[13]{name:"Seven Databases in Seven Weeks"} | Node[56]{name:"7つのデータベース 7つの世界"} | :Translate[13]{language:"Japanese"} | +--------------------------------------------------------------------------------------------------------------------------+ 1 row Nodes created: 1 Relationships created: 1 Properties set: 2 Labels added: 1 48 ms
値の取得では関係のプロパティで指定することもできます。
$ MATCH (n)-[{language: 'Japanese'}]->(m) RETURN n, m; +------------------------------------------------------------------------------------+ | n | m | +------------------------------------------------------------------------------------+ | Node[13]{name:"Seven Databases in Seven Weeks"} | Node[56]{name:"7つのデータベース 7つの世界"} | +------------------------------------------------------------------------------------+ 1 row 28 ms
プロパティの追加と削除
SET 節で追加が、REMOVE 節で削除ができます。
$ MATCH (n {name: 'Eric'}) SET n.surname = 'Redmond' RETURN n; +-----------------------------------------+ | n | +-----------------------------------------+ | Node[11]{name:"Eric",surname:"Redmond"} | +-----------------------------------------+ 1 row Properties set: 1 40 ms
$ MATCH (n {name: 'Eric'}) REMOVE n.surname RETURN n; +-----------------------+ | n | +-----------------------+ | Node[11]{name:"Eric"} | +-----------------------+ 1 row Properties set: 1 81 ms
+=
演算子を使って追加することもできます。一度に複数のプロパティを追加するときに便利です。
$ MATCH (n {name: 'Eric'}) SET n += {surname: 'Redmond'} RETURN n; +-----------------------------------------+ | n | +-----------------------------------------+ | Node[11]{name:"Eric",surname:"Redmond"} | +-----------------------------------------+ 1 row Properties set: 1 77 ms
ここまで作成したノードと関係を確認します。
$ MATCH (n) RETURN n; +-------------------------------------------------+ | n | +-------------------------------------------------+ | Node[11]{name:"Eric",surname:"Redmond"} | | Node[12]{name:"Jim"} | | Node[13]{name:"Seven Databases in Seven Weeks"} | | Node[56]{name:"7つのデータベース 7つの世界"} | +-------------------------------------------------+ 4 rows 31 ms
$ MATCH (n)-[r]->(m) RETURN r; +-------------------------------------+ | r | +-------------------------------------+ | :Write[0]{} | | :Write[1]{} | | :WrittenBy[5]{} | | :WrittenBy[4]{} | | :Translate[13]{language:"Japanese"} | +-------------------------------------+ 5 rows 22 ms
削除
ノードあるいは関係を取得したのちに DELETE することで削除することができます。
$ MATCH (e)-[{language: 'Japanese'}]->(j) DELETE j; +-------------------+ | No data returned. | +-------------------+ Nodes deleted: 1 36 ms TransactionFailureException: Transaction was marked as successful, but unable to commit transaction so rolled back.
ここでノードを削除しようとしていますが、失敗しました。先に関係を削除する必要があるようです。
関係とノードを一緒に削除してみます。
$ MATCH (e)-[r {language: 'Japanese'}]->(j) DELETE r, j; +-------------------+ | No data returned. | +-------------------+ Nodes deleted: 1 Relationships deleted: 1 47 ms
$ MATCH (n) RETURN n; +-------------------------------------------------+ | n | +-------------------------------------------------+ | Node[11]{name:"Eric",surname:"Redmond"} | | Node[12]{name:"Jim"} | | Node[13]{name:"Seven Databases in Seven Weeks"} | +-------------------------------------------------+ 3 rows 16 ms
無事削除できました。
いつか読むはずっと読まない:また別のクエリ言語
待望の SPARQL 本です。以前仕事で SPARQL に触れた時にこの本があったらと思わずにはいられないのですが、執筆陣はその仕事でお世話になった先生方。つまりその仕事の結果としてこの本があるわけで…。
わたし自身は現在はその仕事からはずれていますが、またいつ加わることになるかもしれないので、今のうちに読んで勉強しておきます(幾分業務連絡)。
オープンデータ時代の標準Web API SPARQL (NextPublishing)
- 作者: 加藤文彦,川島秀一,岡別府陽子,山本泰智,片山俊明
- 出版社/メーカー: インプレスR&D
- 発売日: 2015/11/13
- メディア: オンデマンド (ペーパーバック)
- この商品を含むブログ (1件) を見る