Search code examples
sqlruby-on-railsarel

Nested sql queries in rails when :has_and_belongst_to_many


In my application I the next task that has not already been done by a user. I have Three models, A Book that has many Tasks and then I have a User that has has and belongs to many tasks. The table tasks_users table contains all completed tasks so I need to write a complex query to find the next task to perform.

I have came up with two solutions in pure SQL that works, but I cant translate them to rails, thats what I need help with

SELECT * FROM `tasks`
WHERE `tasks`.`book_id` = @book_id
AND `tasks`.`id` NOT IN (
    SELECT `tasks_users`.`task_id`
    FROM `tasks_users`
    WHERE `tasks_users`.`user_id` = @user_id)
ORDER BY `task`.`date` ASC
LIMIT 1;

and equally without nested select

SELECT *
FROM tasks
LEFT JOIN tasks_users
    ON tasks_users.tasks_id = task.id
    AND tasks_users.user_id = @user_id
WHERE tasks_users.task_id IS NULL
AND tasks.book_id = @book_id
LIMIT 1;

This is what I Have done in rails with the MetaWhere plugin

book.tasks.joins(:users.outer).where(:users => {:id => nil})

but I cant figure out how to get the current user there too,

Thanks for any help!


Solution

  • I think this will duplicate the second form with the LEFT JOIN:

    class Task < ActiveRecord::Base
      scope :next_task, lambda { |book,user| book.tasks.\
        joins("LEFT JOIN task_users ON task_users.task_id=tasks.id AND task_users.user_id=#{user.id}").\
        where(:tasks=>{:task_users=>{:task_id=>nil}}).\
        order("date DESC").limit(1) }
    end
    

    Note that instead of tasks_users this uses the table name task_user, which is more typical for a join model. Also, it needs to be called with:

    Task.next_task(@book_id,@user_id)