I\'m trying to do a simple Post/Tags relation in rails 3. Everything working fine except when I want to query the Posts which are related to several tags. Basically I\'d like to
Ok so I found no way to avoid a find_by_sql. Here is what I've done.
@posts = Post.find_by_sql([ "SELECT * FROM posts p
JOIN (
SELECT pt.post_id FROM posts_tags pt
JOIN posts p ON p.id = pt.post_id
JOIN tags t ON t.id = pt.tag_id
WHERE t.label IN (?)
GROUP BY pt.post_id
HAVING count(pt.post_id) = ?
) ct ON c.id = ct.post_id", names_array, names_array.size])
I personally don't completely understand this query (found on http://www.sergiy.ca/how-to-write-many-to-many-search-queries-in-mysql-and-hibernate/ - #3). Especially the part where it joins a select. So if anyone could explain how really work this query it would be great.
Further more, if anyone knows how to do this in a more "rails" way (than a hard coded query), I'd love it.
Hope this helps some people.
I don't know if it will solve you problem but for complex queries like that I almost always just use Squeel.
Then do something like this:
@posts = Post.joins(:tags)
.where{tags.name.like_any names_array}
.group("post_id")
.having("count(post_id) = #{names_array.size}")
The SQL hopefully looks something like this
SELECT "posts".* FROM "posts"
INNER JOIN "tags" ON "tags"."post_id" = "posts"."id"
WHERE (("tags"."name" LIKE "TagA" OR "tags"."name" LIKE "TagB"))
GROUP BY post_id
HAVING count(post_id) = 2
If I remember squeel is pretty good at using ILIKE instead of LIKE depending on the database used. (atleast better than AR)
Also you could do this using AR without squeel but I REALLY like some of the ideas and helpers that come with squeel like _all
As for an explination...
Assume I searched for TagsA and B.
What that does is finds all the Posts with those tags.
So you'll have something like:
Then it will group all those different Post results by the joined tags using post_id.
Then it will check the number of Tags the SQL line has by checking how many forgien_ids are present. Since A and B have 2 tags you know it matched all you input.
I'm stuck on the same problem. The squeel docuemtnation suggests this is possible. In the Compound Conditions section he lists three ways to do something similar.
Given
names = ['Ernie%', 'Joe%', 'Mary%']
Then you can do
Person.where('name LIKE ? OR name LIKE ? OR name LIKE ?', *names)
or
Person.where((['name LIKE ?'] * names.size).join(' OR '), *names)
or
Person.where{name.like_any names}
The documentation implies we can use AND instead of OR or like_all as opposed to like_any.
However I can't seem to get it to work for a habtm relationship. It keeps giving me undefined method 'call'
for an instance of ActiveRecord.