【问题标题】:How can I make this Ruby on Rails page more efficient?如何使这个 Ruby on Rails 页面更高效?
【发布时间】:2008-12-23 00:25:07
【问题描述】:

我正在建立一个网站,用户可以在其中跟踪他们收集的《龙与地下城》人物 (www.ddmdb.com)。此功能涉及的模型/关系如下:

用户:

  • 身份证
  • 登录(用户名)
  • 一堆其他字段

微型:

  • 身份证
  • 姓名
  • 数字(集合中的#,不计数)
  • release_id(外键)
  • 一堆其他字段和外键

所有权:

  • id(这真的需要吗?)
  • user_id
  • miniature_id
  • have_count
  • 最喜欢的(布尔值)

我建立的相关关系如下:

用户:

  • has_many :所有权
  • has_many :miniatures, :through => :ownerships, :uniq => true, :conditions => "ownerships.have_count > 0"
  • has_many :favorites, :through => :ownerships, :source => :miniature, :uniq => true, :conditions => "ownerships.favorite = true"

缩影:

  • has_many :所有权
  • has_many :owners, :through => :ownerships, :source => :user, :uniq => true, :conditions => "ownerships.have_count > 0"

所有权:

  • belongs_to :user
  • belongs_to :miniature

我有一个页面,用户既可以查看和更新​​他们的收藏,也可以查看其他用户的收藏。它包含网站上所有微缩模型的列表和每个旁边的文本框,用户可以在其中输入他们拥有的每个微缩模型的数量。此功能也存在于微型模型的子列表中(按类型、发布、大小、稀有度等过滤)

当用户创建帐户时,他们的所有权中没有任何条目。当他们使用收藏页面或微缩模型子列表更新他们的收藏时,我在所有权表中创建条目,仅用于提交页面上的微缩模型。因此,如果它是完整的收藏列表,我会更新所有迷你图(即使计数为 0),或者如果它是子列表,我只会更新那些微缩图。因此,在任何时候,我可能拥有的特定用户: - 没有所有权条目 - 一些微缩模型的条目 - 所有微缩模型的条目。

我遇到的问题是我不知道如何使用“Rails 方法”通过 LEFT JOIN 查询数据库,因此如果用户在所有权中没有微型条目,则默认为have_count 为 0。目前,当我遍历所有缩影时,我分别查询每个 user_id/miniature_id 组合,这显然效率很低。

查看:

<% for miniature in @miniatures %>
  <td><%= link_to miniature.name, miniature %></td>
  <td><%= text_field_tag "counts[#{miniature.id}]", get_user_miniature_count(current_user, miniature), :size => 2 %></td>
<% end %>

助手:

def get_user_miniature_count(user, miniature)
  ownerships = user.ownerships
  ownership = user.ownerships.find_by_miniature_id(miniature.id)
  if ownership.nil?
    return 0
  else
    return ownership.have_count
  end
end

另一种解决方案是在用户注册时为所有微缩模型创建条目,但是当所有用户在注册后将新微缩模型添加到数据库时,我还必须为所有用户添加 0 have_count。这似乎有点复杂,但也许这是正确的方法?

在所有权表中没有该特定用户的条目的情况下,有没有办法进行连接并为缩影提供默认值?

【问题讨论】:

    标签: sql ruby-on-rails activerecord performance


    【解决方案1】:

    我要说的第一件事是,用户模型应该拥有计算出用户拥有多少给定微型模型的代码,因为它看起来像是“业务逻辑”而不是视图格式。

    我的建议是为您的用户模型添加一个方法:

    def owns(miniature_id)
      o = ownerships.detect { |o| o.miniature_id == miniature_id }
      (o && o.have_count) || 0
    end
    

    干编码,ymmv。

    编辑:请注意,一旦加载,所有权就会被 Rails 缓存,并且检测不会像 find 那样被 ActiveRecord 覆盖,因此就像您期望它在数组上一样(即没有数据库操作)。

    【讨论】:

    • 这看起来像我正在寻找的东西,虽然方法名称不是很......在我看来很明显。我期待着所有权.includes 或所有权.contains 之类的东西。我在控制台中尝试了这个,它看起来很有希望。接下来我会用我的应用程序尝试一下。谢谢!
    • 包括?确实是一种方法,但它用于查找完全匹配的对象,您没有要查找的对象,只有该对象上的属性值(尽管该属性恰好在您的范围内是唯一的正在搜索)。 find实际上是detect的同义词。
    • 为了澄清最后一点,find 是在可枚举对象上检测的同义词。我了解 ActiveRecord 会为其对象/集合覆盖此行为,但仅在查找时而不是检测时。 rubycentral.com/book/ref_m_enumerable.html
    • 这有点帮助,但没有得到我希望的提升 - 延迟从 DB 转移到了渲染。它在每个条目中搜索约 1000 行。哈希有帮助吗?之前:2.61783 秒 |渲染:1.14116 (43%) | DB:1.34131 (51%) 之后:2.20406 秒 |渲染:1.87113 (84%) |数据库:0.21206 (9%)
    【解决方案2】:

    使用 fd 的建议和在http://www.ruby-forum.com/topic/52385 找到的信息,我创建了以下方法:

    def miniature_count(miniature_id)
      if @counts.nil?
        @counts = Hash.new
        ownerships.collect{|o| @counts[o.miniature_id] = o.have_count }
      end
      count = @counts[miniature_id] || 0
    end
    

    这最终比 detect 方法更快。

    我选择 miniature_count 而不是 owns 作为名称,因为 owns 听起来像是一种应该返回布尔值而不是整数的方法。

    查询每个条目

    完成于 2.61783 (0 reqs/sec) |渲染:1.14116 (43%) |数据库:1.34131 (51%) | 200 好 [http://ddmdb/collection/1]

    检测方法

    完成于 2.20406 (0 reqs/sec) |渲染:1.87113 (84%) |数据库:0.21206 (9%) | 200 OK [http://ddmdb/collection/1]

    哈希方法

    在 0.41957(2 个请求/秒)内完成 |渲染:0.19290 (45%) |数据库:0.10735 (25%) | 200 OK [http://ddmdb/collection/1]

    我肯定需要添加缓存,但这绝对是一种改进。我还怀疑我过早地优化了这段代码,但它是一个小网站,2.5 秒的加载时间并不让我高兴。

    【讨论】:

    • 干得好,同意方法名称,我只是快速挑选了一些东西。名字很重要,所以我很高兴你找到了更有表现力的东西。
    【解决方案3】:

    也许我遗漏了一些东西,但是您指定关系的方式似乎足以让 rails 自己计算出计数?你试过吗?

    编辑: 重新在 cmets 中讨论...怎么样:

    <% ownerships=current_user.ownerships %> 
    <% for miniature in @miniatures %>
      <td><%= link_to miniature.name, miniature %></td>
      <td><%= text_field_tag "counts[#{miniature.id}]", get_miniature_count(ownerships, miniature), :size => 2 %></td>
    <% end %>
    

    get_miniature_count() 只是遍历提供的所有权并返回 0 或如果微型出现在列表中则返回计数?我认为这将避免在“for”的每次迭代中再次返回数据库。

    编辑 2:我还建议启动脚本/控制台并尝试直接在 ruby​​ 中执行您想要的操作,即根据 ruby​​ 而不是 SQL 来测试所有权列表中的缩影成员资格。通常,rails 和 activerecord 足够聪明,可以在幕后为您执行必要的 SQL 黑魔法,因为它知道这些关系。如果你找到一个用户然后执行 user.methods 你会看到什么是可用的。

    【讨论】:

    • 我可以做 user.ownerships 但这只会列出用户有记录的条目,而不是他没有的条目。
    • 是的,但你不能重复使用它而不是每次都计算它吗?
    • 澄清我的意思,你不能抓住 user.ownerships 然后在整个视图中使用它(使用 ruby​​ 来确定所有权中是否有一个缩影,而不是在每次迭代时都进行查找)。
    • 恐怕我不知道该怎么做。因此问题:-)
    猜你喜欢
    • 2018-07-12
    • 2013-03-31
    • 2010-12-19
    • 2016-06-20
    • 1970-01-01
    • 1970-01-01
    • 2014-10-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多