makrusak
makrusak

Reputation: 459

Join tables - Associations in Rails 3

For example I have 2 tables: topics and comments.

topic has_many comments. comment belongs_to topic.

Comments table have column sender_id where I save user's id, who create this comment.

Task: Select ids of topics, which is not commented by this user (I know user_id). (I want to do it with one query).

Earlier I do it so. ( two queries ) ( My database - PostgreSQL)

incorrect_topics = Comment.select(:topic_id).where(:sender_id => user_id).map { |elem| elem.topic_id }
ids = select(:id).where(["id NOT IN (?)", incorrect_topics]).map { |elem| elem.id }

It works fine, but I suppose that if app become bigger it will be serious problem.

Thanks.

Upvotes: 0

Views: 107

Answers (2)

mu is too short
mu is too short

Reputation: 434665

If all you need are the IDs then you could bypass all the ActiveRecord stuff and do it by hand:

ids = connection.select_rows(%Q{
    select id
    from topics
    where id not in (
        select topic_id
        from comments
        where sender_id = #{user_id.to_i}
    )
}).map { |r| r.first.to_i }

If you're certain the user_id is already an integer then you don't need the .to_i. You could also use #{connection.quote(user_id)}. You could also use the same SQL with select_values if you wanted to avoid unwrapping he rows by hand with a map.

If you wanted the Topic instances then find_by_sql would be an option:

topics = find_by_sql(%q{
    select *
    from topics
    where id not in (
        select topic_id
        from comments
        where sender_id = :user_id
    )
}, :user_id => user_id)

Some things are clearer when you use straight SQL than when you try to wrap it up in ActiveRecord stuff.

Upvotes: 1

Kleber S.
Kleber S.

Reputation: 8240

Maybe something like this could work:

incorrect_topics = Comment.where('sender_id <> ?', user_id).topics

Let me know if helps you anyway.

Upvotes: 0

Related Questions