2013-12-22 30 views
0

我有兩個activerecords類,User和UserSkill。 UserSkill有squeel篩,如下圖所示:ActiveRecords加入條件:如何使用squeel篩選器以不同的條件集合加入同一張表中?

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

這不是我想要的:

實在的,我想是有10一個skill_id也一user_skill,並且用戶也紛紛爲11 skill_id也和值user_skill高於2

ActiveRecords似乎「鏈接」的條件,我總是最終得到一個空的關係,因爲UserSkill不能在同一時間有10和11的ID!

我想以某種方式區分第一個和第二個條件,以便我得到滿足第一個條件和第二個條件的user_skills的用戶,而不是針對同一個user_skill行,而不是當然如何實現這一點!

我認爲別名是要走的路,但是我如何爲每個user_skill指定一個不同的別名,我想使用squeel或ActiveRecord語法進行引用?

我會想象我的問題與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

這是你需要採取交集的用戶集。換句話說,您需要將「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的原因。

我將離開擴展squeel和大於兩套你除了要注意,你可以使用SQL INTERSECT運營商有兩個以上的操作數如下:

query1 INTERSECT query2 INTERSECT query3 ... 
+0

非常感謝彼得的非常詳細的答案 - 從我上次編輯中可以看到,使用循環索引爲連接生成別名似乎可行,但看起來有點蹩腳 - 我認爲如果我用描述的方式使用相交,它會更清晰set1和set2是squeel篩選器 - 我會在這裏進行一下,並會告訴你它是怎麼回事! – mulus