我有两个activerecords类,User和UserSkill。 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的用户技能为10,并且还有一个user_skill,其skill_id为11,且值大于2.
ActiveRecords似乎“链接”条件,我总是以空关系结束,因为UserSkill不能同时拥有10和11的id!
我想以某种方式区分第一个和第二个条件,以便我获得user_skills的用户满足第一个条件和序列中的第二个条件,而不是同一个user_skill行,并且我不确定怎么实现呢!
我认为别名是可行的方法,但是如何为每个我想使用squeel或ActiveRecord语法引用的user_skill指定不同的别名?
我想我的问题与this one非常相似,但是由于我使用的是循环,我认为我不能使用建议的解决方案!
编辑:我也在squeel github上找到了这个,但不知道如何将它应用到我的特定用例:Need a way to alias joins for multiple joins on the same relation
最后编辑:
我已经在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
答案 0 :(得分:0)
这是您需要使用多组用户的情况。换句话说,您需要将“AND”应用于用户组,而不是where
条件。当您链接where
条件时,您正在“处理”条件,这就是您看到结果的原因。
您可以使用Intersection of two relations中描述的基本技术在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,这可以使用this answer from @MikDiet中描述的技术来完成上述问题:
User.connection.unprepared_statement do
User.find_by_sql "#{set1.to_sql} INTERSECT #{set2.to_sql}"
end
在这种情况下,您正在执行单个查询,尽管该查询显然正在“执行更多工作”。您应该阅读原始答案以了解使用unprepared_statement
的原因。
除非要注意您可以使用SQL INTERSECT
运算符和两个以上的操作数,否则我会将扩展名留给squeel和两个以上的set:
query1 INTERSECT query2 INTERSECT query3 ...