neo4j + rails でリレーション色々

neo4jrbのリレーション問題

どうも!記事2本目からいきなりマニアックなところに行ってしまいました。qiitaに書いても良かったけどせっかくなのでここに。開発でrails + neo4jを使い始めて一ヶ月ちょい、neo4jrbの基本的な操作をまとめました。active_rel 使わず、active_nodeオンリーです。間違ってる可能性めっちゃあります。ご注意を。

リレーション云々する前に

model同士の関係性を明記する必要がありますね

# user.rb
class User
include Neo4j::ActiveNode
# 省略
property :name, type: String
property :email, type:String

has_many :out, :edit, type: :user_articles, model_class: :Article

# 省略
end
# article.rb
class Article
include Neo4j::ActiveNode
# 省略
property :title, type: String
property :content, type: String

has_one :in, :author, type: :articles_user, model_class: :User, unique: true

#省略
end

has_manyとかhas_oneのオプションについて左から説明すると、

  • :out, :in => リレーションの向きです。:inに矢印が刺さるので、今回だとUserからArticleへ矢印が伸びています。
  • :edit, :author => rails内でリレーションを呼び出すメソッド(的な?)の名称です。このメソッドをモデルのインスタンスに繋げると、紐づいているインスタンスが配列で帰ってきます。

    tom = User.find_by(name: "tom")
    tom.edit  #  => [article1, article2, article3]
    article = Article.find_by(name: "article2")
    article.author # => [tom]
    


  • :article_user, :user_articles => リレーションの名称です。(neo4j browserで確認できるやつ) しかし、リレーション名は一つしか定められないので、矢印が出ている方(:outの方)に書いた:typeがリレーション名になります。:inの方のモデルに書くtypeに意味があるのかは知りません。追記するかも。とりあえず明示的に対応させておきましょう。

  • model_class => そのままです。リレーション貼る相手のモデル名になります。

  • unique: true => 二つのノードの組み合わせが同じときにはリレーションを新しく作りません。今回の場合で言えば、tom と article1 の間には、user_articlesリレーションは最大一つまでということです。

リレーションを張ろう

まず前提として、neo4jrbではリレーションは配列のように扱います。今回の場合で言えば、tomに結びついているarticle達は、tom.editという配列の中に入っています。新しくArticleモデルのインスタンス(ノード), article4をtomと紐づけたいときは、tom.editの配列の中に入れてやればいいのです。つまり、

article4 = Article.find_by(name: "article4")
tom.edit << article4

こうです。これでリレーションが張れました。調べても出てこねえと思ってたら、公式に普通に乗ってました。説明「you can create associations」のみ。。。。

リレーションを貼る方法がもう一つあって、

article4 = Article.find_by(name: "article4")
tom.edit = article4

これでもいけます。上のと何が違うかというと、tomに紐づいているノードの数が違います。上は、tomに3個Articleのノードが紐づいているとして、4個目を追加する操作ですが、下は既存の3個のリレーションを切って、新しく一個(article4)とリレーションを張ります。配列の更新と思えばわかりやすいかも。上はtom.edit配列にarticle4をpushしてますが、下はtom.edit配列を要素をarticle4だけの配列にする操作です。

リレーションを切ろう

これも配列の操作と考えれば理解できると思います

リレーションを全部切るとき

tom.edit = nil

tom.editを空の配列にすればtomからはuser_articleリレーションは一つも張られていない状態になります。

リレーションを一部切るとき

tom.edit = tom.edit.to_a - tom.edit.where(title: "しっぽりゅーしょん").to_a

tom.edit配列から何個か要素を取り除けばいいので、配列の引き算したものをtom.editにまた代入してやればOKです。
この場合では、tomの書いた記事の中から、タイトルが「しっぽりゅーしょん」の記事とtomとのリレーションを切りました。