How to join with multiple instances of a model when using a join table with active record?

Viewed 177

This is for Rails 5 / Active Record 5 using PostgreSQL.

Let's say I have two models: Product and Widget. A Product has_many widgets and a Widget has_many products via a join table called products_widgets.

I want to write a query that finds all products that are associated with both the widget with id=37 and the widget with id=42.

I actually have a list of ids, but if I can write the above query, I can solve this problem in general.

Note that the easier version of this query is to find all widgets that are associated with either the widget with id=37 or the widget with id=42, which you could write as follows:

Product.joins(:products_widgets).where(products_widgets: {widget_id: [37, 42]})

But that isn't what I need.

1 Answers

As a starter: in pure SQL, you could phrase the query with exists conditions:

select p.*
from product p
where 
    exists (
        select 1 
        from products_widgets pw 
        where pw.product_id = p.product_id and pw.widget_id = 37
    )
    and exists (
        select 1 
        from products_widgets pw 
        where pw.product_id = p.product_id and pw.widget_id = 42
    )

In active Record, we can try and use the raw subqueries directly in where conditions:

product
    .where('exists(select 1 from products_widgets where product_id = product.product_id and widget_id = ?)', 37)
    .where('exists(select 1 from products_widgets where product_id = product.product_id and widget_id = ?)', 42)

I think that using .arel.exist might also work:

product
    .where(products_widgets.where('product_id = product.product_id and widget_id = ?', 37).arel.exists)
    .where(products_widgets.where('product_id = product.product_id and widget_id = ?', 42).arel.exists)
Related