【问题标题】:Rails4: How to add Computed columns in Active recordRails4:如何在活动记录中添加计算列
【发布时间】:2015-04-19 02:37:40
【问题描述】:

我们已经在用户表上找到了一个“数据”列,其中有一个巨大的 json 转储。

现在,每次我们加载一堆用户时,我们都会将所有这些数据放入内存中,从而导致内存不足错误。

我们希望能够编写一些可以在我们的选择语句中使用的计算列。

eg:
Instead of doing this 
user.data['profile_data']['data']['image']

We would like to add a column :image and then write a query like:

Here :name and :email are actual columns on the table and :image is a computed column:
Users.where(SOME_CONDITION).select(:name,:email,:image)

主要用例是我们显示所有用户的索引页面,它基本上为所有用户加载数据列

这样可以避免在内存中加载庞大的数据列,帮助我们从数据列中加载我们想要的字段

在 Rails4 中最好的方法是什么?

更新:

  • 我们在 Heroku 上使用 postgres。

【问题讨论】:

  • 这些都是:name:email:image 列来自data 吗?
  • 不,他们不是。更新了问题“这里 :name 和 :email 是表上的实际列,而 :image 是计算列”
  • 你用的是什么数据库?

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


【解决方案1】:

我会将数据列移动到另一个表,但如果这不是一个选项,请尝试lazy_columns gem。

class User < ActiveRecord::Base
  lazy_load :data
end

现在data 列将在初始加载期间被排除,但如果您尝试访问.data,它将从数据库中检索。

【讨论】:

  • 数据列不会加载到内存中,怎么解决不了?
  • 所以我们在索引视图中显示所有用户。因此,一旦加载该页面,所有数据列就会被加载到内存中。我相信使用lazy_load 只会延迟加载?
【解决方案2】:

使用迁移添加列:image。然后添加以下代码:

class User < ActiveRecord::Base
  before_save :extract_image

  private

  def extract_image
    self.image = self.data['profile_data']['data']['image']
    self.save
  end
end

before_save:在Base.save之前调用(不管是create还是update保存)。

【讨论】:

  • 我明白了。这会起作用..但是..我不希望将新列添加到实际表中,因为 a] 有很多这样的计算字段 b] json 的架构可能会更改,因此会保留在无 schma 的列中。想法?
  • 这是不好的做法,您不想在数据库中存储计算字段。
  • @ricks 好的,你想把它们存放在哪里?为什么将它们存储到数据库是不好的做法?
  • @ArupRakshit 反对数据库规范化,对于大型数据集,您的一个小错误可能会导致很多数据完整性问题,如果修改了计算,您需要更新表以重新计算。此外,为计算字段添加更多列可能会降低性能。也许在特定情况下这没什么大不了的,但我仍然认为这是不好的做法。
  • @ricks 这是一个特定的解决方案问题。当您进行测试时,什么都不会出错。对于一列,我想不出一个表...复杂的计算结果可以被缓存,并与所有可用的回调保持同步。Rails 有 counter cache 是有原因的,并且如果这是赞赏和帮助..我也不能反对我的这个答案建议。
【解决方案3】:

Postgresql 支持两个json data type,如文档中所述

有两种 JSON 数据类型:json 和 jsonb。它们接受几乎相同的值集作为输入。主要的实际区别是效率之一。 json 数据类型存储输入文本的精确副本,处理函数必须在每次执行时重新解析;而 jsonb 数据以分解的二进制格式存储,由于增加了转换开销,因此输入速度稍慢,但处理速度明显加快,因为不需要重新解析。 jsonb 还支持索引,这是一个显着的优势。

所以要解决您的问题,您需要通过迁移将data 列的类型更改为jsonb

# This should use the up and down methods, because change_column 
# is not reversible     
class ChangeUsersDataColumnTypeToJsonb < ActiveRecord::Migration
   def up
      change_column :users, :data, :jsonb
   end
   def down
      change_column :users, :data, :text # or whatever datatype it was
   end
end

而不是使用 postgres 提供的 functions 查询图像字段来查询 json 数据类型:

Users.where(SOME_CONDITION).select(:name,:email,"data::json->'image' as image")

您可以像访问任何其他属性一样访问image 属性。

您还必须将:data 属性定义为lazy loading column,例如 ,以便在实例化用户对象时不会加载该列。

class User < ActiveRecord::Base
  lazy_load :data
end

【讨论】:

    猜你喜欢
    • 2014-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-15
    相关资源
    最近更新 更多