2009-05-10 3 views
1

私のユーザーモデルでは、ユーザーが複数のアカウント(別名「マルチ」)を持っているかどうかを検索したいと考えています。複数のアカウントをより効率的に検出するためにこのクエリを作成するにはどうすればよいですか?

ユーザーにはセッションがあります。セッションは

  1. creator_id =(両方の列は、第1のユーザのIDをセッションとしてログインし、そして最後にユーザの
  2. updater_id = IDは、セッションが

としてログインし設定するようにカスタマイズされています

セッションが保存されたときに2つのセッションが異なることが検出された場合、ユーザーが1つとしてログインしてから別のセッションにログインしたことを意味するため、後のセッション(およびこのクエリ) - 別名、彼らはマルチです。 (再帰的なベースケースをキャッチするために、creator_idは、現在のセッションにリセットされます。)

ここでそれをしないコードがあります:

class Session < ActiveRecord::SessionStore::Session 
    attr_accessor :skip_setters 

    before_save :set_ip 
    before_save :set_user 

    def set_user 
     return true if self.skip_setters 
     # First user on this session 
     self.creator_id ||= self.data[:user_id] 
     # Last user on this session 
     self.updater_id = self.data[:user_id] if self.data[:user_id] 
     if self.creator_id and self.updater_id and 
     self.creator_id != self.updater_id 
     logger.error "MULTI LOGIN: User #{self.creator.login} and \ 
      #{self.updater.login} from #{self.ip}" 

     # Save a copy for later inspection 
     backup = Session.new {|dup_session| 
      dup_session.attributes = self.attributes 
      # overwrite the session_id so we don't conflict with the 
      # current one & it can't be used to log in 
      dup_session.session_id = ActiveSupport::SecureRandom.hex(16) 
      dup_session.skip_setters = true 
     } 
     backup.save 

     # Set this session to be single user again. 
     # Updater is what the user looks for; 
     # creator is the one that's there to trigger this. 
     self.creator_id = self.updater_id 
     end 
    end 

    # etc... e.g. log IP 
end 

次のクエリは、作業を行います。

しかし、これはあまり効率的ではありませんが、Railsの関連メソッドではうまく機能しません。セッションは非常に大きく、頻繁に使用される表であり、ユーザーはほぼ同じです。

これをhas_manyの関連付けに変更したいと思います(すべての関連性のある魔​​法の作品がそうするように)。可能であれば:through => a :multi_sessionsアソシエーション。それは、現在の連合のようなものだけではなく、複数の方向性を捕らえなければならない。

これをどのように改善できますか?

class User < ActiveRecord::Base 
has_many :sessions, :foreign_key => 'updater_id' 

# This association is only unidirectional; it won't catch the reverse case 
# (i.e. someone logged in first as this user and then as the other) 
has_many :multi_sessions, :foreign_key => 'updater_id', 
    :conditions => 'sessions.updater_id != sessions.creator_id', 
    :class_name => 'Session' 
has_many :multi_users, :through => :multi_sessions, 
    :source => 'creator', :class_name => 'User' 

... 

# This does catch both, but is pretty ugly :(
def multis 
    # Version 1 
    User.find_by_sql "SELECT DISTINCT users.* FROM users \ 
    INNER JOIN sessions \ 
     ON (sessions.updater_id = #{self.id} XOR sessions.creator_id = {self.id}) AND \ 
     (sessions.updater_id = users.id XOR sessions.creator_id = users.id) \ 
    WHERE users.id != #{self.id}" 
    # Version 2 
    User.find(sessions.find(:all, :conditions => 'creator_id != updater_id', 
    :select => 'DISTINCT creator_id, updater_id').map{|x| 
     [x.creator_id, x.updater_id]}.flatten.uniq - [self.id]) 
end 
end 
+0

なぜ、セッション中にセッションでユーザーIDを変更させるのですか?そういう理由があると仮定すると(今私を逃している理由)、セッションが変更されたときにセッションが変更されることを単に追跡しないのはなぜですか?それを遡及的に行うことは、人生を不必要に複雑にするようであり、クエリは遅く信頼性がありません。 –

+0

たとえば、ログイン/ログアウトを介してフラッシュメッセージを保存することができます。これには永続セッションが必要です。 (または、2つの「異なる」セッションにまたがってデータの一部を永続させることは、実際にはあまり効果がありません)。 遡及的に行うと、後でセッションデータを見ることができます)、相対的なタイミングなどを知っている、つまり、監査目的。 プラス - 変更するとIDを変更することをI * do *トラックします。これはafter_saveトラップのためのものです。 :-P – Sai

+0

OK:情報のDBMSを掘り下げる必要がないように、変更が発生した 'after_save'トラップレコードを作成します。それが起こったときに必要なものを記録してください。私は、永続的なフラッシュが正しいとあなたの言葉を取る - それはほとんど説得力があると聞こえますが、それは私の専門分野ではありません。 –

答えて

0

私はあなたがセッションの再利用を解決しようとしている問題は奇妙なものだと思います。ログを処理して靴下や複数のアカウントを持つユーザーを検出することで、IPを一意のログインにマッピングする方がはるかに優れています。あなたの問題を解決するために、より信頼性の高い簡単な方法があります。

関連する問題