如何告诉ActiveRecord在where子句之后丢弃关联缓存

时间:2015-01-07 22:11:03

标签: ruby-on-rails caching activerecord

我尝试使用where子句刷新ActiveRecord has_many关联。我发现了两件令我困惑的事情:

  1. 如果您调用关联的位置
  2. ,ActiveRecord不会使用缓存的关联
  3. 强制ActiveRecord通过将true传递给关联方法来查询数据库,将在考虑where子句之前获取所有关联,从而导致许多对象无效且不必要地加载到内存中。
  4. (1)是预期的行为吗? [更新 - 请参阅下面的Frederick Cheung的回答;每个电话会产生一个新的关系,所以我相信答案是肯定的]我无法在文档中找到这个,所以我觉得如果依靠我的观察。如果是,则(2)没有实际意义,因为AR每次都会查询数据库,而不会告诉它强制重新加载。但是,由于我不知道,我有后续问题,如果(1)无法保证:如何在没有立即执行查询的情况下强制重新加载?

    为了说明(输出被抑制,因为它们不相关,只显示AR日志输出):

    MediaFile has_many :assets

    缓存和强制刷新按预期工作:

    m = MediaFile.delivered.offset(20).last
    ### log suppressed
    1.9.3-p448 :078 > m.assets.map(&:id); nil
      MediaAsset Load (0.3ms)  SELECT `media_assets`.* FROM `media_assets` WHERE (`media_assets`.media_file_id = 533849)
    1.9.3-p448 :079 > m.assets.map(&:id); nil
    1.9.3-p448 :080 > m.assets(true).map(&:id); nil
      MediaAsset Load (0.3ms)  SELECT `media_assets`.* FROM `media_assets` WHERE (`media_assets`.media_file_id = 533849)
    

    where子句强制db fetch:

    1.9.3-p448 :081 > m.assets.where(type: "Source").map(&:id); nil
      MediaAsset Load (0.3ms)  SELECT `media_assets`.* FROM `media_assets` WHERE (`media_assets`.media_file_id = 533849) AND (`media_assets`.`type` = 'Source')
    1.9.3-p448 :082 > m.assets.where(type: "Source").map(&:id); nil
      MediaAsset Load (0.3ms)  SELECT `media_assets`.* FROM `media_assets` WHERE (`media_assets`.media_file_id = 533849) AND (`media_assets`.`type` = 'Source')
    

    将true传递给关联方法会强制立即加载:

    1.9.3-p448 :083 > m.assets(true).where(type: "Source").map(&:id); nil
      MediaAsset Load (0.3ms)  SELECT `media_assets`.* FROM `media_assets` WHERE (`media_assets`.media_file_id = 533849)
      MediaAsset Load (0.2ms)  SELECT `media_assets`.* FROM `media_assets` WHERE (`media_assets`.media_file_id = 533849) AND (`media_assets`.`type` = 'Source')
    

    请注意,最后一个示例中的第一个查询首先执行而没有where子句。这似乎不是最理想的。

1 个答案:

答案 0 :(得分:2)

首先,假设where子句强制刷新是不正确的 - 对于活动记录,这两个只是单独的关系,而一个被加载的另一个没有发生。如果保持这种关系,它只会加载一次,例如

rel = m.assets.where(type: 'Source'); nil
rel.map(&:id)
rel.map(&:id)

只能访问数据库一次。

您可以通过调用reset来清除关联缓存:

m.assets.reset

(但是,由于这会返回关联,因此在控制台中这将触发重新加载,除非您附加;false或类似内容)