Rails 3 many to many query condition

前端 未结 3 570
名媛妹妹
名媛妹妹 2021-01-22 20:36

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

相关标签:
3条回答
  • 2021-01-22 21:15

    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.

    0 讨论(0)
  • 2021-01-22 21:24

    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:

    • PostA TagA
    • PostA TagB
    • PostB TagA
    • PostB TagB
    • PostC TagA

    Then it will group all those different Post results by the joined tags using post_id.

    • PostA TagA TagB
    • PostB TagA TagB
    • PostC TagA

    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.

    0 讨论(0)
  • 2021-01-22 21:33

    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.

    0 讨论(0)
提交回复
热议问题