I have a table called variants, which is joined to another table called option_values in a many-to-many relationship through a table called option_values_variants.
I would like to pull all the variants which have a specific subset of ids in the joined option_values table. This is easier to demonstrate with an example:
Table data:
variants
id | name
--------------
1 | Tennant
2 | Smith
3 | Eccleston
4 | Hurt
option_values
id | name
---------
80 | blue
81 | red
82 | green
83 | 64 gb
84 | 128 gb
85 | 256 gb
86 | short
87 | long
option_values_variants
variant_id | option_value_id
----------------------------
1 | 80
1 | 85
1 | 86
2 | 82
2 | 85
2 | 87
3 | 80
3 | 84
3 | 86
4 | 81
4 | 83
4 | 86
In this example, I want to get all the variants which have option values "blue" and "short", specifically (so option value ids 80 and 86).
The expected result should be variants "Tennant" and "Eccleston", since those variants have both "blue" and "short", despite having one different option value each. "Hurt" has "short" but does not have "blue", so he should be excluded, and "Smith" has neither.
I seem to be having a bit of a brain block in terms of forming this query, either within the Rails context, or in plain SQL. I cannot seem to figure out how to specify that "the join should be made only when the ids of the joining table match or are a subset of the target parameter list". This is my current attempt at the moment:
@product.variants.joins(:option_values).where(option_values: {id: [80, 86]}).uniq
Which produces the SQL query:
SELECT DISTINCT `variants`.* FROM `variants`
INNER JOIN `option_values_variants` ON `option_values_variants`.`variant_id` = `variants`.`id`
INNER JOIN `option_values` ON `option_values`.`id` = `option_values_variants`.`option_value_id`
WHERE `variants`.`product_id` = 32 AND `option_values`.`id` IN (80, 86)
Since the query is performing a WHERE ... IN ()
, it does not produce the desired result. Using the above example, I would return "Tennant", "Eccleston", and "Hurt", however "Hurt" should not belong because it does not contain the option value "blue" (or option value id 80).
How can I select records where the joined table rows match or are a subset of a given list of ids?
Additionally, it may be a result of said brain block, but admittedly I may not be explaining this very well. Please let me know if I am not making any sense