5

course has_many tags by has_and_belongs_to, now given two id of tags, [1, 2], how to find all courses that have those both two tags

Course.joins(:tags).where("tags.id IN (?)" [1, 2]) will return record that have one of tags, not what I wanted

# app/models/course.rb
has_and_belongs_to_many :tags



# app/models/tag.rb
has_and_belongs_to_many :courses
William Herry
  • 1,430
  • 14
  • 26

3 Answers3

7

Since you're working with PostgreSQL, instead of using the IN operator you can use the ALL operator, like so:

Course.joins(:tags).where("tags.id = ALL (?)", [1, 2])

this should match all ids with an AND instead of an OR.

Vapire
  • 4,568
  • 3
  • 24
  • 41
  • still not work: `Course.joins(:tags).where("tags.id ALL (?)", [1, 2])` `: SELECT "courses".* FROM "courses" INNER JOIN "courses_tags" ON "courses_tags"."course_id" = "courses"."id" INNER JOIN "tags" ON "tags"."id" = "courses_tags"."tag_id" WHERE (tags.id ALL (1,2)) ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR: syntax error at or near "ALL" LINE 1: ...gs"."id" = "courses_tags"."tag_id" WHERE (tags.id ALL (1,2)) ^ ` – William Herry May 20 '14 at 10:33
  • Thanks, I forgot the `,` after the where string. – Vapire May 20 '14 at 10:47
  • I think you need to use an operator, as well… edited my answer. – Vapire May 21 '14 at 09:04
  • Use PostgreSQL array syntax: `Course.joins(:tags).where("tags.id = ALL (?)", '{1, 2}')` – Vlad Hilko Sep 20 '17 at 11:51
3

This is not a single request, but might still be as quick as other solutions, and can work for any arbitrary number of tags.

tag_ids = [123,456,789,876] #this will probably come from params
@tags = Tags.find(tag_ids)
course_ids = @tags.inject{|tag, next_tag| tag.course_ids & next_tag.course_ids} 
@courses = Course.find(course_ids)
Max Williams
  • 32,435
  • 31
  • 130
  • 197
0

referring to @Vapire's answer

Course.joins(:tags).where("tags.id = ALL ('{?}')", [1, 2])