Meglio:
Person.includes(:friends).where( :friends => { :person_id => nil } )
Per l'hmt è sostanzialmente la stessa cosa, fai affidamento sul fatto che una persona senza amici non avrà contatti:
Person.includes(:contacts).where( :contacts => { :person_id => nil } )
Aggiornare
Hai una domanda has_onenei commenti, quindi solo l'aggiornamento. Il trucco qui è che si includes()aspetta il nome dell'associazione ma ilwhere aspetta il nome della tabella. Per a has_onel'associazione sarà generalmente espressa al singolare, in modo che cambi, ma la where()parte rimane così com'è. Quindi, se Personsolo has_one :contactallora la tua affermazione sarebbe:
Person.includes(:contact).where( :contacts => { :person_id => nil } )
Aggiornamento 2
Qualcuno ha chiesto del contrario, amici senza gente. Come ho commentato di seguito, questo in realtà mi ha fatto capire che l'ultimo campo (sopra: il :person_id) in realtà non deve essere correlato al modello che stai restituendo, deve solo essere un campo nella tabella di join. Lo saranno tutti, nilquindi può essere uno di loro. Questo porta a una soluzione più semplice di quanto sopra:
Person.includes(:contacts).where( :contacts => { :id => nil } )
E poi cambiando questo per restituire gli amici senza persone diventa ancora più semplice, cambi solo la classe in primo piano:
Friend.includes(:contacts).where( :contacts => { :id => nil } )
Aggiornamento 3 - Rotaie 5
Grazie a @Anson per l'eccellente soluzione Rails 5 (dategli alcuni +1 per la sua risposta di seguito), potete usare left_outer_joinsper evitare di caricare l'associazione:
Person.left_outer_joins(:contacts).where( contacts: { id: nil } )
L'ho incluso qui, così la gente lo troverà, ma per questo merita i +1. Grande aggiunta!
Aggiornamento 4 - Rotaie 6.1
Grazie a Tim Park per aver sottolineato che nella prossima 6.1 puoi farlo:
Person.where.missing(:contacts)
Grazie al post a cui è collegato anche lui.