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とのリレーションを切りました。