Rails 使用 fetch 进行缓存

Rails caching using fetch

Rails.cache.fetch(plan_list_cache_key(project), expires_in: EXPIRES_TIME) do
      plans = Plan.all.where(is_active: true)
      project_plan = project.project_plan
      hash_array = []
      plans.each do |plan|
        plan = plan.attributes
        expired = if (plan["id"] == project.plan.id)
                    (project_plan.end_date.to_date - Date.current).to_i.positive? ? false : true
                  else
                    false
                  end
        subscribed = plan["id"] == project.plan.id
        hash_array << plan.merge!({is_expired: expired , subscribed: subscribed})
      end
      hash_array
    end

型号:

class Plan < MysqlBase
  has_many :project_plans
end

class ProjectPlan < MysqlBase
  belongs_to :plan
  belongs_to :project
end

class Project < MysqlBase
  has_one :project_plan
  has_one :plan, :through => :project_plan
end

架构:

create_table "plans", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
    t.string "name"
    t.text "description"
    t.integer "email_limit"
    t.integer "validity"
    t.float "unit_price", limit: 24
    t.string "currency"
    t.integer "base_user_count"
    t.boolean "is_renewable"
    t.boolean "is_active"
    t.boolean "is_free"
    t.string "terms"
    t.integer "trial_days"
    t.boolean "default"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

create_table "project_plans", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
    t.integer "plan_id"
    t.integer "project_id"
    t.datetime "start_date"
    t.datetime "end_date"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

回应

{
    "status": "OK",
    "status_code": 200,
    "message": "ok",
    "data": {
        "plans": [
            {
                "id": 1,
                "name": "Free",
                "description": "30 days free trial",
                "email_limit": 10000,
                "validity": 30,
                "unit_price": 0.0,
                "currency": "USD",
                "base_user_count": 0,
                "trial_days": 30,
                "is_free": true,
                "subscribed": false,
                "is_expired": false
            },
            {
                "id": 2,
                "name": "Standard",
                "description": " for 100 users",
                "email_limit": 0,
                "validity": 30,
                "unit_price": 0.03,
                "currency": "USD",
                "base_user_count": 100,
                "trial_days": 0,
                "is_free": false,
                "subscribed": true,
                "is_expired": false
            }
        ]
    }
}

每个项目都有一个单独的 project_plan。计划对所有用户都是一样的。但我要做的是添加两个额外的字段 is_expired & subscribed 和 API response

我正在使用 fetch 在此处缓存我的响应。但这是进行缓存的正确方法吗,因为有一些数据库和逻辑操作

如果这不是正确的方法,这里会发生什么类型的错误?

进行这种缓存的最佳方法是什么?

此处的一个解决方案是加入项目计划 table 和 select 聚合:

SELECT 
  plans.*,
  -- you will get integers since MySQL is a peasant database and does not have real boolean types
  COUNT(pp.id) > 0 AS subscribed,
  COALESCE(MAX(pp.ends_at) < NOW(), FALSE) AS is_expired
FROM plans
LEFT OUTER JOIN project_plans pp 
  ON pp.plan_id = plans.id
  AND pp.project_id = ?
GROUP BY plans.id
class Plan
  def with_statuses_for_project(project)
    pp = ProjectPlan.arel_table
    j = pp.join(arel_table).on(
      pp[:plan_id].eq(arel_table[:id])
                  .and(
                    pp[:project_id].eq(project.id)
                  )
    )
    .select(
      arel_table[Arel.star],
      "COUNT(pp.id) > 0 AS subscribed",
      "COALESCE(MAX(pp.ends_at) < NOW(), FALSE) AS is_expired"
    )
    .joins(j.join_sources)
    .group(:id)
  end
end