简体   繁体   English

Rails4:如何在活动记录中添加计算列

[英]Rails4: How to add Computed columns in Active record

We have landed ourself with a column 'data' on User table that has a huge json dump in it. 我们已经在User表上放置了“ data”列,其中有一个巨大的json转储。

Now every time we load a bunch of users we get all this data into the memory resulting into out of memory errors. 现在,每次我们加载一堆用户时,我们会将所有这些数据都存储到内存中,从而导致内存不足错误。

We want to be able to write some computed columns that we can use in our select statements. 我们希望能够编写一些可在select语句中使用的计算列。

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)

The main use case is the index page where we display all users which basically loads data column for all users 主要用例是索引页面,我们在其中显示所有用户,这基本上为所有用户加载数据列

This will avoid loading the huge data column in memory and help us load the fields we want from the data column 这样可以避免将巨大的数据列加载到内存中,并帮助我们从数据列加载所需的字段

Whats the best way to do this in Rails4? 在Rails4中执行此操作的最佳方法是什么?

Updates: 更新:

  • We use postgres on Heroku. 我们在Heroku上使用postgres。

I would move the data column to another table, but if that's not an option try the lazy_columns gem. 我会将数据列移到另一个表,但是如果不是这样,请尝试使用lazy_columns gem。

class User < ActiveRecord::Base
  lazy_load :data
end

Now the data column will be excluded during the initial load, but if you try to access the .data it will be retrieved from the database. 现在,在初始加载期间, data列将被排除,但是,如果您尝试访问.data ,则会从数据库中检索它。

Add the column :image using the migration . 使用migration添加栏:image Then add the below code : 然后添加以下代码:

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 : Is called before Base.save (regardless of whether it's a create or update save). before_save :在Base.save之前Base.save (无论是create还是update保存)。

Postgresql support two json data type , as described in the documentation Postgresql支持两种json数据类型 ,如文档中所述

There are two JSON data types: json and jsonb. 有两种JSON数据类型:json和jsonb。 They accept almost identical sets of values as input. 他们接受几乎相同的值集作为输入。 The major practical difference is one of efficiency. 实际的主要区别是效率之一。 The json data type stores an exact copy of the input text, which processing functions must reparse on each execution; json数据类型存储输入文本的精确副本,处理函数必须在每次执行时重新解析; while jsonb data is stored in a decomposed binary format that makes it slightly slower to input due to added conversion overhead, but significantly faster to process, since no reparsing is needed. jsonb数据以分解后的二进制格式存储,由于增加了转换开销,因此输入速度稍慢,但由于不需要解析,因此处理速度明显更快。 jsonb also supports indexing, which can be a significant advantage. jsonb还支持索引编制,这可能是一个很大的优势。

So to solve your problem, you need to change the type of the data column to jsonb through a migration: 因此,要解决您的问题,您需要通过迁移将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

than to query the image field with you use the functions that postgres provides to query the json data type : 您可以使用postgres提供的功能来查询json数据类型:

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

than you access the image attribute like any other attribute. 您访问image属性的方式与其他任何属性一样。

You have also to define the :data attribute as a lazy loading column , like , so that column do not get loaded when the user object is instantiated. 您还必须将:data属性定义为类似的延迟加载列 ,以便在实例化用户对象时不会加载该列。

class User < ActiveRecord::Base
  lazy_load :data
end

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM