gpt4 book ai didi

ruby-on-rails - 如何在递归方法中使用预加载集合

转载 作者:数据小太阳 更新时间:2023-10-29 07:00:35 27 4
gpt4 key购买 nike

我有以下自指关联:

class Action < ActiveRecord::Base
# self referential association
has_many :action_parents
has_many :parents, through: :action_parents
has_many :action_children, class_name: 'ActionParent', foreign_key: 'parent_id'
has_many :children, through: :action_children, source: :action

def should_finish
should_start + duration
end

def should_start
# my_start is a field in db: if there are no parents (root) it will use this field
return my_start if parents.empty?
parents.map(&:should_finish).sort.last
end
end

我的问题是 should_finishshould_start 正在互相调用,即使我预加载了父级,它仍然会导致许多查询:

Action.includes(:parents).last.should_finish
# a new query every time it checks for parents

关于如何缓存 actionsparents 有什么想法吗?

编辑 - 让我提供一些背景信息:

# actions table:        actions_parents table:
# id | duration task_id | parent_id
# 1 | 5 2 | 1
# 2 | 10 3 | 1
# 3 | 20 4 | 2
# 4 | 15 4 | 3
#
# |--------------|
# | action 2 |
# |---------- >| duration: 10 |
# | |--------------|
# | |
# |--------------| |--------->|--------------|
# | action 1 | | action 4 |
# | duration: 5 | | duration: 15 |
# |--------------| |--------->|--------------|
# | |
# | |--------------|
# |----------->| action 3 |
# | duration: 20 |
# |--------------|

PS:没有循环依赖

假设我有一个 my_start 字段 some day at 10:00:00:

# action | should_start | should_finish
# -------------------------------------
# 1 | 10:00:00* | 10:00:05
# 2 | 10:00:05 | 10:00:15
# 3 | 10:00:05 | 10:00:25
# 4 | 10:00:25** | 10:00:40
#
# * value from db since there is no parent
# ** should_finish of parent with latest should_finish (action 3)

我认为它可以使用 Action.includes(:parents) 预加载所有 Action

最佳答案

在我知 Prop 体细节之前,我会抛出一个疯狂的,

假设父结构中没有显着的循环,除了缓存整个表之外,您无法通过缓存任何内容来帮助自己,因为每次您点击 parent 时,您都会为每个 Action 实例点击不同的 parent 并且没有任何缓存策略(包括 Rails 缓存策略)可以帮助您将整个数据集移动到缓存中。

事实是,你似乎试图做的事情实际上很难用关系数据库来做,而且似乎正是发明图形数据库的原因(参见 What are graph databases & When to use a graph databaseNeo4j on Heroku )

除了使用图形数据库或缓存整个操作表之外,您可以做的最好的事情是优化查询(使用 pluck)并可能将它们重写为 PLSQL 函数。

B 计划是利用您对数据的了解来拯救您,

  • should_startdurationshould_finish 中的值有变化吗?变化很大吗?
  • 数据实时重要吗? (即时不时收到稍微过时的信息是可以的)
  • 您构建数据的方式是否需要更便于读取或写入?
  • 引出问题:将它们设为 Action 模型的数据库字段是否有意义,这样您就不必每次查找时都遍历?
    • 即你做的读操作比写操作多得多吗?
    • 您可以在后台作业中重新计算计算字段
  • 您是否经常在短时间内访问 should_startshould_finish
  • 你对 Neo4j 感觉如何? :D
  • ....

编辑 1

我目前看到的唯一解决方案是取消递归问题。试试这个:

例如,在字符串/文本字段中存储父结构的 ID

  • Action 4 将有 [1,2,3],
  • Action 2 和 3 将有 [1]
  • 操作 1 将有 []

然后当您将 ancestor_ids 数组映射到 id => action 的散列时

def ancestry_hash
@ancestry_hash ||= Hash[ Action.includes(:action_parents).where(id: ancestor_ids).map{|action| [action.id, action]} ]
end

然后重新实现递归查询以遍历此哈希而不是 activerecord 树,否则您将触发其他查询。像这样的东西:

def should_finish(id = self.id)
should_start(id) + ancestry_hash[id].duration
end

def should_start(id = self.id)
# my_start is a field in db: if there are no parents (root) it will use this field
action = ancestry_hash[id]
return my_start if action.action_parents.empty?
action.action_parents.pluck(:parent_id).map{ |parent_id| should_finish(parent_id) }.sort.last
end

我没有测试代码,但我希望你明白了,它应该足够接近这个

关于ruby-on-rails - 如何在递归方法中使用预加载集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35018882/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com