Search code examples
ruby-on-railsruby-on-rails-4activerecordvirtual-attribute

How do I do where queries across virtual attributes?


I have a Node model, that has a virtual attribute user_tags.

This is what it looks like at the console:

[42] pry(main)> n = Node.first
  Node Load (0.5ms)  SELECT  "nodes".* FROM "nodes"   ORDER BY "nodes"."id" ASC LIMIT 1
=> #<Node id: 6, name: "10PP Form Video", family_tree_id: 57, user_id: 57, media_id: 118, media_type: "Video", created_at: "2015-03-09 20:57:19", updated_at: "2015-03-09 20:57:19", circa: nil, is_comment: nil>
[43] pry(main)> n.user_tags
  ActsAsTaggableOn::Tag Load (0.3ms)  SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" = $1 AND "taggings"."taggable_type" = $2 AND "taggings"."context" = 'user_tags'  [["taggable_id", 6], ["taggable_type", "Node"]]
=> [#<ActsAsTaggableOn::Tag id: 6, name: "[email protected]", taggings_count: 1>, #<ActsAsTaggableOn::Tag id: 4, name: "[email protected]", taggings_count: 1>]

What I would like to do, is create a scope on my Node model, to map to just those nodes that have user_tags. i.e. where !user_tags.empty?.

How do I do that?


Solution

  • scope :with_tags, ->() { joins(:tags).uniq }
    

    You cannot use where as tags are stored in a separate table than your model - you have to make a join with this other table first. Now the lovely part - joins executes the INNER JOIN which means it will not load models which has no matching records in the other table. Now all that is left is to get rid of duplicates (if you have models with n tags, JOIN will return that record n times)