Assume I have the following models:
class Article < ActiveRecord::Base
has_many :keyword_mappings, :as => :entity, :dependent => :delete_all
has_many :keywords, -> {select('keywords.*, keyword_mappings.count as count')}, :through => :keyword_mappings
end
class Keyword < ActiveRecord::Base
belongs_to :entity, polymorphic: true
has_many :keyword_mappings, :dependent => :delete_all
end
class KeywordMapping < ActiveRecord::Base
belongs_to :keyword
belongs_to :entity, polymorphic: true
validates :keyword_id, :uniqueness => {
:scope => [:entity_id, :entity_type]
}
end
The intention behind the custom select in the has_many :keywords for Article is that I can loop through an article's keywords while checking the count, individually retrieve the count, or pass it to a Javascript app with the count being part each keyword in a JSON response. count is a field of the keyword_mappings table, which servers as a join table / join model object between the keywords and the articles.
(I found this solution from these two answers.)
With this setup, if I call .includes(:keywords) on a set of articles which contains at least one article that has associated keyword mappings, I get this SQL error:
2.1.1 :001 > Article.order('id desc').limit(1).includes(:keywords)
Article Load (0.7ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`deleted_at` IS NULL ORDER BY id desc LIMIT 1
KeywordMapping Load (0.7ms) SELECT `keyword_mappings`.* FROM `keyword_mappings` WHERE `keyword_mappings`.`entity_type` = 'Article' AND `keyword_mappings`.`entity_id` IN (20813)
Keyword Load (1.2ms) SELECT keywords.*, keyword_mappings.count as count FROM `keywords` WHERE `keywords`.`id` IN (2)
Mysql2::Error: Unknown column 'keyword_mappings.count' in 'field list': SELECT keywords.*, keyword_mappings.count as count FROM `keywords` WHERE `keywords`.`id` IN (2
It seems that it's attempting to access the count field in a separate query from the one in which it access the keyword mappings table, whereas with accessing a single article's keywords (which succeeds), it joins on both:
2.1.1 :002 > Article.last.keywords
Article Load (0.7ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`deleted_at` IS NULL ORDER BY `articles`.`id` DESC LIMIT 1
Keyword Load (0.9ms) SELECT keywords.*, keyword_mappings.count as count FROM `keywords` INNER JOIN `keyword_mappings` ON `keywords`.`id` = `keyword_mappings`.`keyword_id` WHERE `keyword_mappings`.`entity_id` = 20813 AND `keyword_mappings`.`entity_type` = 'Article'
I'd like to use eager loading in this case, while retaining results that do not have any keywords -- is there any way to force .includes() to recognize this pattern and successfully load in the join table field? Or perhaps some way to alter the custom SQL in the has_many to ensure it always joins with the keyword_mappings table?