Reputation: 362
The relationship between the model 'battle' to the model 'category' is has_and_belongs_to_many. I'm trying to find all the battles that don't relate to a specific category. What I tried to do so far is:
Battle.all.includes(:categories).where("(categories[0].name NOT IN ('Other', 'Fashion'))")
This error occurred: ActiveRecord::StatementInvalid: PG::DatatypeMismatch: ERROR: cannot subscript type categories because it is not an array
Thanks, Rotem
Upvotes: 1
Views: 205
Reputation: 3245
The use of categories[0].name
is not a valid SQL reference to the name
column of categories.
Try this:
Battle.includes(:categories).reject do |battle|
['Other', 'Fashion'].include? battle.categories.map(&:name)
end
Note that this code performs two queries - one for Battle
and one for Category
- and eager loads the appropriate Category
Active Record objects into the instantiated Battle
records' :categories
relationship. This is helpful to prevent N+1 queries as described in details in the guides.
Also note that the code above first loads to memory ALL Battle
records before eliminating the ones that have the undesired categories. Depending on your data, this could be prohibitive. If you prefer to restrict the SQL query so that only the relevant ActiveRecord objects get instantiated, you could get away with something like the following:
battle_ids_sql = Battle.select(:id).joins(:categories).where(categories: {name: ['Other' ,'Fashion']}).to_sql
Battle.where("id NOT IN (#{battle_ids_sql})")
battle_ids_sql
is an SQL statement that returns battle IDs that HAVE one of your undesired categories. The SQL which is actually executed fetches all Battle
records that are not in that inner SQL. It's effective, although use sparingly - queries like this tend to become hard to maintain rather quickly.
You can learn about joins
, includes
and two other related methods (eager_load
and preload
) here.
Upvotes: 2