I am trying to find a complement using ActiveRecord and/or SQL.
I have a collection of 'annotations' that each have two relevant fields:
- session_datum_id which corresponds to the user who performed the annotation. Null means it has not yet been done.
- post_id which represents the post the annotation is 'about'. Cannot be null.
There are potentially multiple annotations per post_id.
I would like to efficiently find an annotation that satisfies two constraints:
- session_datum_id is null. This means this particular annotation hasn't already been performed.
- the session_datum passed in as an arg has not already performed another annotation with the same post_id.
Here is a very naive version which does a join outside the DB. It finds all annotations this user has already performed and removes those post_ids from the exhaustive list of annotations that still need to be performed. It then picks at random from the resulting list:
def self.random_empty_unseen(session_datum)
mine = where('session_datum_id = ?', session_datum)
elligible = where('session_datum_id IS NULL')
mine.each do |i|
elligible.each do |j|
if (i.post_id == j.post_id)
elligible.delete(j)
end
end
end
elligible[rand(elligible.count)]
end
As the list of annotations gets large, this will bog down terribly. I can imagine a probabilistic algorithm where we select an elligible annotation at random and then checks if the user has already performed it (retrying if so) but there are degenerate cases where that won't work. (Large number of annotations and the user has performed all but one of them.)
Is there a closed form query for this, perhaps using NOT EXISTS?