如何基于关联模型的虚拟属性创建高性能范围?

时间:2015-05-29 00:26:30

标签: ruby-on-rails ruby-on-rails-4 activerecord

我有一个printContent('Ticket12345')模型。每个Node对象都有一个名为nodeuser_tags的虚拟属性,该属性继承自user_tag_list

看起来像这样:

acts_as_taggable

我想要做的是创建一个范围,该范围返回已在节点上标记的所有用户记录。即他们的电子邮件会显示在某些[24] pry(main)> n => #<Node id: 85, name: "House Fire 2", family_tree_id: 57, user_id: 57, media_id: 228, media_type: "Video", created_at: "2015-05-15 00:20:26", updated_at: "2015-05-20 01:06:34", circa: nil, is_comment: nil, cached_votes_total: 0, cached_votes_score: 0, cached_votes_up: 0, cached_votes_down: 0, cached_weighted_score: 0, cached_weighted_total: 0, cached_weighted_average: 0.0> [25] pry(main)> n.user_tags ActsAsTaggableOn::Tag Load (4.0ms) 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", 85], ["taggable_type", "Node"]] => [#<ActsAsTaggableOn::Tag id: 4, name: "gerry@test.com", taggings_count: 1>, #<ActsAsTaggableOn::Tag id: 6, name: "danny@test.com", taggings_count: 1>] [26] pry(main)> n.user_tag_list => ["gerry@test.com", "danny@test.com"] n.user_tags的列表中。

我试过这个:

n.user_tag_list

这个问题是这只适用于1 scope :with_tagged_users, -> (node) { node.user_tag_list.collect do |tag| User.find_by email: tag end } 个对象。即我必须做node,这有点违背了在整个模型上使用范围的目的。

我还可以对所有节点进行迭代,然后检查每个节点上是否Node.tagged_users(node),但这似乎并不高效。

我觉得应该有一种方法可以使用user_tags.empty?joins或其他一些应该能够简单地执行此操作的内置方法。很快,但我无法想出来。

因此,为了清楚起见,理想情况下我想做includes并且应该返回在任何节点上标记的所有用户的列表,最好没有重复。

4 个答案:

答案 0 :(得分:1)

我不知道acts_as_taggable的确切表格,但我认为它们的名称是:“taggings”和“tags”

知道你可以这样做:

  • 获取所有代码

    ActsAsTaggableOn::Tag.where("id in (?)", ActsAsTaggableOn::Tagging.where(taggable_type: 'Node').pluck(:tag_id))
    
  • 获取具有标记的节点

    Node.where("id in (?)", ActsAsTaggableOn::Tagging.where(taggable_type: 'Node').pluck(:taggable_id))
    
  • 你可以在类方法中
  • def self.with_tagged_users
      Node.where("id in (?)", ActsAsTaggableOn::Tagging.where(taggable_type: 'Node').pluck(:taggable_id))
    end
    

答案 1 :(得分:0)

我认为这只是一个实例方法而不是范围,但这里的范围是第一个:

scope :with_tagged_users, -> (node) { User.find_by email: node.user_tag_list }

同样,这里的查询是Node上的实例方法:

def tagged_users
  User.find_by email: user_tag_list
end

答案 2 :(得分:0)

您可能会发现squeel gem很有帮助。我认为它的过滤器&#39; sifter&#39;功能可能允许您实现目标。我有空的时候会尝试一个例子。

答案 3 :(得分:0)

上述Cristi方法的替代方案只是添加名为cache_column的{​​{1}}。

然后我必须确保在添加和删除标记时添加逻辑以递增或递减。

但在我的范围内,我只是做了类似的事情:

cached_num_user_tags

所以,授予......它对实际属性进行查询,而不是像我最初问过的那样的虚拟属性....但它给了我一个可行的解决方案。