Reputation: 5314
I have two models : User and Rsvp. User has many Rsvps.
I want to get all Users that has no Rsvp created at a specified date.
So i wrote this :
scope :free, -> (date) { joins(:rsvps).where.not(rsvps: {created_at: date.beginning_of_day..date.end_of_day}) }
But it only works if User has at least one Rsvp but not for 'date'. It doesn't work when a User has no Rsvp at all.
I tried with a left outer join, but it doesn't work. I must miss something :
scope :free, -> (date) { joins("LEFT OUTER JOIN rsvps ON rsvps.user_id = users.id").where.not(rsvps: {created_at: date.beginning_of_day..date.end_of_day}) }
Here's my unit test :
it 'should fetch user if he has no rsvp at all' do
# Given
user = User.create!(email: '[email protected]', password: '12345678', password_confirmation: '12345678')
# Then
expect(User.free(Date.new(2014, 1, 1)).count).to eq 1
end
Upvotes: 1
Views: 1699
Reputation: 46
This should work:
scope :free, -> (date) { includes(:rsvps).where("rsvps.id IS NULL OR rsvps.created_at NOT BETWEEN ? AND ?", date.beginning_of_day, date.end_of_day).references(:rsvps) }
Hope it helps!
Upvotes: 3
Reputation: 3638
You almost got it right. You need to use a GROUP_BY statement so you don't get repeated rows.
scope :free, -> (date) do
joins("LEFT OUTER JOIN rsvps ON rsvps.user_id = users.id")
.where.not(rsvps: {created_at: date.beginning_of_day..date.end_of_day})
.group("users.id") # or just .group(:id)
end
Upvotes: 1
Reputation: 1183
It's normally that joins
returns Users having at least one Rsvp. That's why I suggest using includes
. Try this one (probably you have to modify the condition because I'm not sure it's what you want exactly):
scope :free, -> (date) { includes(:rsvps).where('rsvps.created_at != ?', date ).references(:rsvps) }
Upvotes: 1