LoginSignup
4
1

More than 3 years have passed since last update.

Neo4jをActiveRecord感覚で扱えるActiveGraphを触ってみた

Posted at

こんにちは。@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ライフをお過ごしくださいー

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1