简体   繁体   中英

Querying a has_and_belongs_to_many association in Ruby

OK. What I am trying to achieve is based on generating a list of related articles based on a specific article and I want to get these related articles based on the categories that are associated with the article. I have a simple habtm relationship between these two models: article and category (join table is named articles_categories ). I want to create a related articles query based on articles that have the same category_id s as one article. For example, article #1 is associated with category #1 and category #2. I want to write a query that grabs the category ids of article #1, then queries the article model for all articles that are associated with those same categories, category #1 and category #2. I have tried everything under the sun and can't seem to figure this out. I have tried .join statements of various kinds and nothing seems to work. I tried to break it down into two parts below:

I can grab all of the category ids of the specific article that I want to reference like this:

article_cat_ids = @article.category_ids

The second part would be the article filter based on these category ids but I can't figure this part out.

So my question is, how do I get an array of articles that are associated with the category ids in the array article_cat_ids . And as a bonus kicker, I would love to exclude the specific article that I originally reference to create the article_cat_ids so that the list of articles does not include the article that I am trying to get related articles for.

Any help would be appreciated.

Sounds interesting. I think your data model is great.

Let's try doing it in SQL first. That will guide us to an ActiveRecord solution.

select a1.id
from articles a1
inner join articles_categories ac1 on ac1.article_id = a1.id
inner join articles_categories ac2 on ac2.category_id = ac1.category_id
inner join articles a2 on a2.id = ac2.article_id
where a2.id = $1
  and a1.id <> $1;

This works, but you could also use a IN-clause subquery. You should do some benchmarking to see which performs better with your data.

Now, how do we translate this into ActiveRecord? Honestly, you might not want to. You're going to end up writing a lot of SQL anyway.

article_id = 1 # or whatever
Article.
  joins('articles_categories ac1 on ac1.article_id = articles.id').
  joins('articles_categories ac2 on ac2.category_id = ac1.category_id').
  joins('articles a2 on a2.id = ac2.article_id').
  where(['a2.id = ? and a1.id <> ?', article_id])

AR is a leaky abstraction in these situations. This should work, but it's less readable than plain SQL in my opinion.

Note that this solution is a single query. Beware of other solutions that involve multiple queries. It's easy to end up with a 1+N query problem here if you use, eg a loop in ruby.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM