2015-07-07 10 views
9

Sto costruendo un'app per appuntamenti in cui la User s può approvare altri utenti. Io uso un modello Approval per tracciare queste relazioni. Ogni Approval ha un user_id e un approved_id - l'ID utente dell'User approvato. Ha anche rejected_at, un datetime per indicare che uno User ne ha rifiutato un altro.Programmazione di DatingApp: associazione ActiveRecord per la ricerca di utenti privi di approvazioni o di approvazioni a 1 via.

Per presentare agli utenti idonei alla current_user, devo interrogare per gli utenti per i quali vi sono o

  • No Approval rapporto
  • Approval rapporto solo con il approved_id come current_user.id (che significa l'Utente ammissibili approva il current_user ma non c'è relazione inversa
  • Escludi User s che hanno un Approval con un attributo non uguale a zero rejected_at dove approved_id è il User o user_id è il current_user.

Come posso creare una query di ActiveRecord per trovare gli utenti idonei? Capisco che posso fare un joins su Approval ma voglio anche tenere conto del fatto che non esiste la relazione Approval tra User s! Sto pensando che potrebbe avere più senso fare solo 2 domande distinte, ma mi piacerebbe sapere se è possibile combinare in un unico ..

+0

Stai cercando una query che soddisfi tutti e tre o uno dei tre? – Pavan

+0

Tutti e 3 bult c'è un XOR con il primo e il secondo. Credo che il n. 1 e il n. 2 siano possibili con una clausola OR – quantumpotato

risposta

6

Il comportamento che si desidera è un LEFT OUTER JOIN, che includerà le righe da users se oppure non ci sono righe corrispondenti in approvals. In questo modo, ottieni uno User che non ha emesso alcun Approval sul tuo target User o uno che ha e possiamo filtrare per il rifiuto.

The query sarebbe simile

-- Looking for users who either have no opinion or have approved user 1 
SELECT * 
FROM users 
LEFT OUTER JOIN approvals 
    ON users.id = approvals.user_id 
    AND approvals.approved_id = 1 -- Filter for only approvals of user 1 
WHERE users.id != 1 -- Ignore user 1 
    AND (
    approvals.approved_id IS NULL -- Approving user has no opinion of user 1 
    OR approvals.rejected_at IS NULL -- Approving user has not rejected user 1 
) 
; 

In pezzi,

  • users LEFT OUTER JOIN approvals considera tutti users, anche se non hanno approvals
  • ON users.id = approvals.user_id coppie users con approvals hanno creato
  • ON ... AND approvals.approved_id = 1 considera solo approvals per User 1
  • WHERE users.id != 1 considera unico altro users
  • WHERE ... approvals.approved_id IS NULL prende users che non hanno creato alcunapprovals pertinenza User 1.
  • WHERE ... approvals.rejected_at IS NULL prende users che ha creare un Approval per User 1, e non era un rifiuto

ActiveRecord non fa nulla di particolarmente carina quando traduciamo questo, ma funziona:

class User < ActiveRecord::Base 
    def eligible_partners 
    User.joins(# Join like we did before 
     <<-SQL 
     LEFT OUTER JOIN approvals 
     ON users.id = approvals.user_id 
     AND approvals.approved_id = #{self.id} 
     SQL 
    ).where.not(id: id) # Ignore ourselves 
    .where(# Filter for no approvals or a positive approval 
     <<-SQL 
     approvals.approved_id IS NULL 
     OR approvals.rejected_at IS NULL 
     SQL 
    ) 
    end 
end 

Se si desidera qualcosa di più leggibile, è possibile raccogliere gli ID di ogni User che ha rifiutato una determinata persona e quindi ottenere tutti gli altri User s. Da un lato, sono due domande; dall'altra, può essere meno costoso di un JOIN una volta che i tuoi tavoli diventano grandi, ed è molto più semplice da capire.

def other_eligible_partners 
    rejecter_ids = Approval 
     .where(approved_id: id) # About ourselves 
     .where.not(rejected_at: nil) # Rejected us 
     .pluck(:user_id) 
    User.where.not(id: rejecter_ids + [id]) # People who didn't reject us 
    end 
Problemi correlati