こんにちは。@mshibuyaです。
グラフデータベースNeo4j向けのActiveModel準拠のOGM(Object Graph Mapping, ORMのグラフ版ですね)であるActiveGraphを触ってみる機会があったのでご紹介します。
なおこの記事はRails Advent Calendar 2020の6日目のエントリーです。
グラフデータベースとは
データ構造としてのグラフを扱うことに最適化されたデータベースシステムです。ノードとエッジから表現され、様々な物事同士の関連関係を効果的に扱うことができます。Amazon Neptuneとしてフルマネージドのグラフデータベースサービスも登場したりしていて、徐々に利用範囲を広げつつあるのではないでしょうか。
とはいえ、このへんの話はあんまり詳しくないので深くは語れないです。なんかグラフ理論ってかっこよさげだしよくない?程度のノリです…
ActiveGraphはグラフデータベースNeo4jをバックエンドとして利用します。もともとNeo4j.rbと呼ばれていたものが最近名前が変わったようですね。
触ってみる
Docker ComposeでNeo4jを立ち上げます。ActiveGraphが対応済みであるバージョン4.0を選択
version: '3'
services:
neo4j:
image: neo4j:4.0
ports:
- "7474:7474"
- "7687:7687"
volumes:
- ./neo4j:/data
neo4j-ruby-driverに必要なseaboltをinstall
$ brew install michael-simons/homebrew-seabolt/seabolt
rails newする
$ rails new graph_app -T -O -m https://raw.githubusercontent.com/neo4jrb/activegraph/master/docs/activegraph.rb
config/neo4j.ymlを作成
development:
url: bolt://localhost:7687
test:
url: neo4j://localhost:7688
モデルを作成
app/model/person.rb
class Person
include ActiveGraph::Node
property :name
end
app/model/friendship.rb
class Friendship
include ActiveGraph::Relationship
from_class :Person
to_class :Person
property :since, type: DateTime
end
このへんで Neo4j::Driver::Exceptions::AuthenticationException (Permission denied)
が出て困ったんですが、Neo4j側でパスワードを変更したら出なくなりました。とりあえず深追いはしません。。
rails consoleから遊んでみます
irb(main):001:0> john = Person.create name: 'john'
CYPHER CREATE (n:`Person`) SET n = $props RETURN n | {:props=>{:uuid=>"ae128a36-4e0b-4879-9213-9d5ec0b0b726", :name=>"john"}}
BOLT: 0ms
irb(main):002:0> paul = Person.create name: 'paul'
CYPHER CREATE (n:`Person`) SET n = $props RETURN n | {:props=>{:uuid=>"5c8987da-de09-4673-b366-a002b491be65", :name=>"paul"}}
BOLT: 0ms
irb(main):003:0> Friendship.create(from_node: john, to_node: paul, since: Time.now)
BOLT: 0ms
BOLT: 0ms
CYPHER
MATCH
(from_node),
(to_node)
WHERE
(ID(from_node) = $from_node_id) AND
(ID(to_node) = $to_node_id)
CREATE (from_node)-[rel:`FRIENDSHIP` $rel_create_props]->(to_node)
SET rel.`since` = $setter_rel_since
RETURN rel | {:from_node_id=>0, :to_node_id=>2, :setter_rel_since=>1607243457, :rel_create_props=>{}}
BOLT: 0ms
=> #<Friendship since: Sun, 06 Dec 2020 08:30:57 +0000>
irb(main):004:0> people = 1.upto(100).map {|i| Person.create name: "person#{i}" }
...
irb(main):005:0> 300.times.each { Friendship.create from_node: people.sample, to_node: people.sample }
...
irb(main):006:0> Person.query_as(:p).where('size((p)-->()) > 5').count # 5人以上friendのいるPersonの数を取得
CYPHER
MATCH (p:`Person`)
WHERE (size((p)-->()) > 5)
RETURN count(*)
BOLT: 0ms
=> 6
ちょっと凝ったクエリをしようとするとNeo4jのクエリ言語であるCypherの知識が求められはするんですが、なかなか面白いです。最短距離を求めるだとか、リコメンデーションに利用したりとか想像は膨らみます…
本当は最近RailsAdminに寄せられたNeo4j対応を試してみるところまで行きたかったのですが、時間がないので今日はここまでで。
よいRailsライフをお過ごしくださいー