使用after_save在Neo4J模型中创建关系

时间:2014-10-23 23:16:32

标签: ruby-on-rails facebook-graph-api neo4j koala-gem neo4j.rb

所以我为这些问题看起来多么荒谬而道歉。我是铁杆的新手,作为第一项任务,我也引进了Neo4J,因为如果我发展这个项目,它似乎是最合适的。

我将解释操作流程,然后显示一些示例代码。我现在尝试在步骤3-5中添加。

  1. 用户通过FB登录
  2. 首次登录会创建一个用户节点。如果用户存在,则只检索该用户+节点
  3. 创建用户节点后,使用koala gem访问FB Graph API
  4. 使用该应用程序检索每位朋友的好友列表。
  5. 浏览每个朋友并在两个用户之间添加双向友谊关系
  6. 由于3-5只需要在用户第一次加入时发生,我想我可以在与after_save回调相关的方法中执行此操作。这个逻辑有一个缺陷,因为我需要在某个时候用其他属性更新用户,它会再次调用after_save。我可以通过更新来防止这种情况发生吗?

    SessionsController供参考

      def create
        user = User.from_omniauth(env["omniauth.auth"])
        session[:user_id] = user.id  
        redirect_to root_url
      end
    
      def destroy
        session.delete(:user_id)
        redirect_to root_path
      end
    

    所以在我的user.rb中我有这样的东西

     has_many :both, :friendships
    
      after_save :check_friends
    
    
      def self.from_omniauth(auth)
        @user = User.where(auth.slice(:provider, :uid)).first
    
        unless @user
          @user = User.new
          # assign a bunch of attributes to @user
    
          @user.save!
        end
        return @user
      end
    
      def facebook
        @facebook ||= Koala::Facebook::API.new(oauth_token)
    
        block_given? ? yield(@facebook) : @facebook
          rescue Koala::Facebook::APIError => e
          logger.info e.to_s
          nil
      end
    
      def friends_count
        facebook { |fb| fb.get_connection("me", "friends", summary: {}) }
      end
    
      def check_friends(friendships)
        facebook.get_connection("me", "friends").each do |friend|
          friend_id = friend["id"]
          friend_node = User.where(friend_id)
          Friendship.create_friendship(user,friend_node)
          return true
        end
      end
    

    friendship.rb

      from_class User
      to_class   User
      type 'friendship'
    
      def self.create_friendship(user,friend_node)
        friendship = Friendship.create(from_node: user, to_node: friend_node)
      end   
    

    我不确定我是否在如何创建关系节点的正确轨道上。在我刚刚创建@user时,如何将其合并到我的check_friends方法中并正确检索用户和朋友节点,以便将两者链接在一起。

    目前它还不知道user和friend_user是节点

    如果您发现其他不良代码,请告诉我们!

    事先:感谢@subvertallchris的帮助。我相信你会回答我的很多问题。

1 个答案:

答案 0 :(得分:5)

这是一个非常好的问题!我认为你已走上正轨,但有一些事情你可以改变。

首先,您需要调整has_many方法。你的关联总是需要终止于一个节点,而不是ActiveRel类,所以你需要重写它,如下所示:

has_many :both, :friends, model_class: 'User', rel_class: 'Friendship'

否则你会遇到一些问题。

您可能需要考虑重新命名您的关系类型,以符合Neo4j风格的一致性。我有很多不好的例子,如果我给你不好的想法,那就很抱歉。 FRIENDS_WITH将是一个更好的关系名称。

至于处理你的大问题,你可以在这里做很多事情。

编辑!废话,我忘记了最重要的部分!抛弃after_save回调并使加载存在/创建新用户行为两种方法。

class SessionsController < ApplicationController
  def create
    user = User.from_omniauth(env["omniauth.auth"])
    @user = user.nil? ? User.create_from_omniauth(env["omniauth.auth"]) : user
    session[:user_id] = @user.id
    redirect_to root_url
  end

  def destroy
    session.delete(:user_id)
    redirect_to root_path
  end
end


class User
  include Neo4j::ActiveNode
  # lots of other properties
  has_many :both, :friends, model_class: 'User', rel_class: 'Friendship'

  def self.from_omniauth(auth)
    User.where(auth.slice(:provider, :uid)).limit(1).first
  end

  def self.create_from_omniauth(auth)
    user = User.new
    # assign a bunch of attributes to user
    if user.save!
      user.check_friends
    else
      # raise an error -- your user was neither found nor created
    end
    user
  end

  # more stuff
end

这将解决你的问题,让它开始。您可能希望将整个事物包装在事务中,因此请在Wiki中阅读。

但我们还没有完成。让我们看一下原来的check_friends

def check_friends(friendships)
  facebook.get_connection("me", "friends").each do |friend|
    friend_id = friend["id"]
    friend_node = User.where(friend_id)
    Friendship.create_friendship(user,friend_node)
    return true
  end
end

你实际上没有把它作为一个论据,所以摆脱它。另外,如果您知道自己只是在寻找单个节点,请使用find_by。我假设每个用户都有一个facebook_id属性。

def check_friends
  facebook.get_connection("me", "friends").each do |friend|
    friend_node = User.find_by(facebook_id: friend["id"])
    Friendship.create_friendship(user,friend_node) unless friend_node.blank?
  end
end

create_friendship方法应该返回true或false,所以只要使方法的最后一个语句执行该操作,您就可以返回它返回的内容。这很容易:

def self.create_friendship(user, friend_node)
  Friendship.new(from_node: user, to_node: friend_node).save
end

create不返回true或false,它返回结果对象,因此将save链接到新对象将获得您想要的结果。除非您打算在方法中更多地使用它,否则您不需要在那里设置变量。

此时,您可以轻松地向ActiveRel模型添加after_create回调,该回调将在from_node上执行某些操作,该活动始终是您刚刚创建的用户。您可以从那里更新用户的属性。控制这种行为正是ActiveRel存在的原因。

我可能还会再做一点工作。首先将facebook内容移动到模块中。它会让您的用户模型更清洁,更专注。

# models/concerns/facebook.rb

module Facebook
  extend ActiveSupport::Concern

  def facebook
    @facebook ||= Koala::Facebook::API.new(oauth_token)

    block_given? ? yield(@facebook) : @facebook
      rescue Koala::Facebook::APIError => e
      logger.info e.to_s
      nil
  end

  def friends_count
    facebook { |fb| fb.get_connection("me", "friends", summary: {}) }
  end
end

# now back in User...

class User
  include Neo4j::ActiveNode
  include Facebook
  # more code...
end

你的模特很容易变成这些凌乱的抓包。很多博客会鼓励这一点。战胜冲动!

这应该是一个好的开始。如果您有任何问题或者我搞砸了什么问题,请告诉我,有很多代码,我可能需要澄清或调整一些代码。希望它有所帮助。