0

我有两个活动记录类,用户和用户技能。UserSkill有一个squeel sifter,如下图:

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))

这不是我想要的:

实际上,我希望用户的 user_skill 的技能 ID 为 10,并且用户技能的技能 ID 为 11 且值高于 2。

ActiveRecords 似乎“链接”了条件,并且我总是以空关系结束,因为 UserSkill 不能同时具有 10 和 11 的 id!

我想以某种方式区分第一个条件和第二个条件,以便我让具有 user_skills 的用户按顺序满足第一个条件和第二个条件,而不是相同的 user_skill 行,并且不知道如何实现那!

我认为别名将是要走的路,但是如何使用 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

在这种情况下,您需要获取用户集的交集。换句话说,您需要将“AND”应用于用户集,而不是where条件。当您链接where条件时,您正在“与”条件,这就是您看到您所看到的结果的原因。

您可以在 Ruby 或 SQL 中执行此操作,使用两个关系的交集中描述的基本技术。为简单起见,我将在此处使用两组进行说明,并将扩展留给两个以上的组,并为您说明。

首先,让我们定义两个集合:

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

在这种情况下,您正在执行两个单独的 SQL 查询并获取结果的交集。

如果您想使用 SQL 进行交集,则需要编写 SQL,这可以使用@MikDiet对上述问题的回答中描述的技术来完成:

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

在这种情况下,您正在执行单个查询,尽管该查询显然在做“更多工作”。您应该阅读原始答案以了解使用unprepared_statement.

我将把扩展名 squeel 和两个以上的集合留给您,但请注意您可以将 SQLINTERSECT运算符与两个以上的操作数一起使用,如下所示:

query1 INTERSECT query2 INTERSECT query3 ...
于 2013-12-22T15:47:15.747 回答