0

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: "danny@test.com", taggings_count: 1>, #<ActsAsTaggableOn::Tag id: 4, name: "gerry@test.com", 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?

marcamillion
  • 32,933
  • 55
  • 189
  • 380
  • You can try `scope :no_tags, lambda { where('user_tags = ?', nil) }` or `scope :no_tags, lambda { where("user_tags IS NULL") }` – Sonalkumar sute Mar 12 '15 at 09:43
  • @Sontya I tried the first, and got this error: `> Node.where('user_tags = ?', nil) Node Load (0.9ms) SELECT "nodes".* FROM "nodes" WHERE (user_tags = NULL) PG::UndefinedColumn: ERROR: column "user_tags" does not exist LINE 1: SELECT "nodes".* FROM "nodes" WHERE (user_tags = NULL)`. I also tried the second and got the same issue. Am I getting this error because I am trying it at the command line, rather than putting it in a lambda on the scope? – marcamillion Mar 12 '15 at 09:47
  • @Sontya Nope the lambda is not the issue. I just tried it with the actual scope on the model, and I got the same error. – marcamillion Mar 12 '15 at 09:49

1 Answers1

2
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)

BroiSatse
  • 44,031
  • 8
  • 61
  • 86
  • Ahhh...genius. This works. Who woulda thunk that `joins` works on virtual attributes too. That's kewl! Thanks much meng! – marcamillion Mar 12 '15 at 09:52
  • `tags` is not a virtual attribute - it is an association. – BroiSatse Mar 12 '15 at 09:57
  • So `acts_as_taggable` adds it as an association? I didn't add any explicit associations, and the gem seems to indicate that it added virtual attributes, but I could be wrong. – marcamillion Mar 12 '15 at 09:58
  • 1
    Yees, it adds this associations and then add a virtual attribute `tag_list`. BTW, be extremely careful with `tag_list`, it uses `pluck` internally so quite often causes N+1 problem. – BroiSatse Mar 12 '15 at 10:01
  • @BroiSatse Interesting. Thanks for the heads-up. I am just using `tags` and not `tag_list`. Will that have the same problem? – marcamillion Mar 12 '15 at 10:19
  • 1
    `tags` is a regular association, which can be preloaded using `includes` to avoid N+1, so no issue there. – BroiSatse Mar 12 '15 at 10:34