0

User と UserSkill という 2 つのアクティブ レコード クラスがあります。UserSkill には、以下に示すように、スクイール シフターがあります。

class User < ActiveRecord::Base
  has_many   :user_skills
end

class UserSkill < ActiveRecord::Base
  belongs_to :user
  belongs_to :skill

  sifter :with_skill_id_and_value_of_at_least do |params|
    if params[1].present?  
      skill_id.eq(params[0]) & value.gteq(params[1])
    else
      skill_id.eq(params[0])
    end
  end
end

私は現在検索を実装しており、コントローラーにこれがあります:

  def search
    users = User.skip_user(current_user)

    if params[:user_skills].present?
      params[:user_skills].each do |user_skill|
        #user_skill is an array where [0] is the id, and [1] the minimum value. 
        #if [1] is empty, it will default to 0 in the sifter

        users = users.joins(:user_skills).where do
         {
            user_skills: sift(:with_skill_id_and_value_of_at_least, user_skill)
         }
        end
      end 
    end   
  end

私の問題は、基本的に、関係が適用されると、次のSQLになることです。

SELECT "users".* FROM "users" 
  INNER JOIN "user_skills" ON "user_skills"."user_id" = "users"."id" 
  WHERE "user_skills"."skill_id" = 10 
  AND (("user_skills"."skill_id" = 11 AND "user_skills"."value" >= 2))

これは私が望むものではありません:

実際、skill_id が 10 の user_skill を持つユーザーと、skill_id が 11 で値が 2 より大きい user_skill を持つユーザーが必要です。

ActiveRecords は条件を「連鎖」しているように見えますが、UserSkill は同時に 10 と 11 の ID を持つことができないため、常に空の関係になってしまいます!

同じ user_skill 行ではなく、最初の条件と 2 番目の条件を順番に満たす user_skills を持つユーザーを取得できるように、最初の条件と 2 番目の条件をどうにかして区別したいと思いますが、達成方法がわかりません。それ!

エイリアスを使用するのがよいと思いますが、squeel または ActiveRecord 構文を使用して参照したい user_skill ごとに異なるエイリアスを指定するにはどうすればよいですか?

私の質問はこれと非常に似ていると思いますが、ループを使用しているため、提案されたソリューションを使用できないと思います!

編集: squeel github でもこれを見つけましたが、特定のユース ケースに適用する方法がわかりません:同じリレーションで複数の結合の結合をエイリアスする方法が必要です

最終編集:

私は動作するSQLでソリューションを書きましたが、ご覧のとおり、非常に醜いです:

      params[:user_skills].each_with_index do |user_skill,index|
         users = users.joins(",user_skills as u#{index}")
                      .where("u#{index}.skill_id= ? AND u#{index}.value >= ?"
                      ,user_skill[0].to_i, user_skill[1].to_i)
      end 
4

1 に答える 1

0

これは、一連のユーザーの交差を取得する必要がある場合です。つまり、where条件ではなく、ユーザーのセットに「AND」を適用する必要があります。条件を連鎖whereさせると、条件を「AND」することになります。これが、表示されている結果を表示する理由です。

これは、Ruby または SQL で行うことができます。2 つの関係の交差 で説明されている基本的な手法を使用します。簡単にするために、ここでは 2 つのセットを使用して両方を説明し、2 つ以上のセットへの拡張はお任せします。

まず、2 つのセットを定義しましょう。

set1 = User.joins(:user_skills).where('user_skills.skill_id = 10')
set2 = User.joins(:user_skills).where('user_skills.skill_id = 11 and user_skills.value >= 2')

注: テキストの例では「2 より大きい値」と表示されていましたが、コードでは「2 以上の値」と表現されていました。私は明らかに上記の後者の解釈を使用しました。

Ruby で共通部分を取得する場合は、&次のように演算子を使用して、結果の配列の共通部分を取得します。

set1 & set2

この場合、2 つの別個の SQL クエリを実行し、結果の交点をとっています。

SQL を使用して交差点を取りたい場合は、SQL を作成する必要があります。これは、@MikDietから前述の質問へのこの回答で説明されている手法を使用して実行できます。

User.connection.unprepared_statement do
  User.find_by_sql "#{set1.to_sql} INTERSECT #{set2.to_sql}"
end

この場合、単一のクエリを実行していますが、そのクエリは明らかに「より多くの作業」を行っています。を使用する理由を理解するには、元の回答を読む必要がありunprepared_statementます。

INTERSECT次のように、SQL演算子を 3 つ以上のオペランドで使用できることに注意してください。

query1 INTERSECT query2 INTERSECT query3 ...
于 2013-12-22T15:47:15.747 に答える