Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ruby-on-rails-3/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails ActiveRecord:从主键数组加载相应的记录数组(保留顺序、重复、最大化性能)_Ruby On Rails_Ruby On Rails 3_Activerecord_Eager Loading - Fatal编程技术网

Ruby on rails ActiveRecord:从主键数组加载相应的记录数组(保留顺序、重复、最大化性能)

Ruby on rails ActiveRecord:从主键数组加载相应的记录数组(保留顺序、重复、最大化性能),ruby-on-rails,ruby-on-rails-3,activerecord,eager-loading,Ruby On Rails,Ruby On Rails 3,Activerecord,Eager Loading,Was:在ActiveRecord中反向加载 我有一个奇怪的问题,我知道我需要使用渴望加载,但由于这是一个如此奇怪的用例,它不能很好地工作 守则: 请注意,可以有相同的任务。现在我想,像在函数式编程中一样,映射列表,这样我就可以得到实际任务,而不是ID,以及它们之间的关联 type_a_tasks = [Task #1, Task #2, etc.] type_b_tasks = [Task #1, Task #2, etc.] 我知道我可以通过 Task.includes(:project)

Was:在ActiveRecord中反向加载

我有一个奇怪的问题,我知道我需要使用渴望加载,但由于这是一个如此奇怪的用例,它不能很好地工作

守则:

请注意,可以有相同的任务。现在我想,像在函数式编程中一样,映射列表,这样我就可以得到实际任务,而不是ID,以及它们之间的关联

type_a_tasks = [Task #1, Task #2, etc.]
type_b_tasks = [Task #1, Task #2, etc.]
我知道我可以通过

Task.includes(:project).find(task_a_tasks.concat(task_b_tasks))

但随后我将其缩减为一组任务,并失去了集合的顺序。这更清楚吗?

我想我看到了你的问题,那就是如果你有一堆任务都属于同一个项目,那么你将多次加载该项目

假设您已经有了一个任务对象数组,这个怎么样

project_ids = @tasks.map{|task| task.project_id}.uniq
@projects = Project.find(project_ids)

让我们先从最明显的方法开始:

type_a_task_ids = [1,2,3,1,2,3]
type_b_task_ids = [1,2,2,3,3] 
type_a_tasks = type_a_task_ids.map { |task_id| Task.includes(:project).find(task_id) }
type_b_tasks = type_b_task_ids.map { |task_id| Task.includes(:project).find(task_id) }
上述方法简单易读,但速度可能较慢:它将为每个不同的任务id执行一次数据库往返,并为给定任务中的每个不同的项目id执行一次数据库往返。所有的延迟加起来,所以您希望批量加载任务和相应的项目

如果您可以让Rails批量加载预取并预先缓存这些相同的记录,比如说,两次往返(一次用于所有不同的任务,一次用于所有不同的关联项目),然后使用与上面完全相同的代码,那就太好了——除了find总是命中缓存而不是数据库

不幸的是,在Rails中,默认情况下情况并非如此,就像ActiveRecord一样。在任务之后运行Task.find1 SELECT*FROM tasks,其中id=1。在任务之后运行find[1,2,3]SELECT*FROM tasks,其中id位于1,2,3将不会利用查询缓存,因为第一个查询与第二个查询不同。不过,第二次、第三次等运行Task.find1将利用查询缓存,因为Rails将多次看到完全相同的SELECT查询并返回缓存的结果集

输入缓存。标识映射缓存的不同之处在于,它基于每个表和主键缓存记录,而不是查询。因此,运行Task.find[1,2,3]将在表任务的标识映射缓存中填写三条记录,分别是ID为1、2和3的条目,随后的Task.find1将立即返回表任务和ID为1的缓存记录

但是,IdentityMap和

如何在没有IdentityMap的情况下获得相同的结果?简单:

# prefetch all distinct tasks and their associated projects
# store the result in our own identity cache
my_tasks_identity_map = \
  Hash[Task.includes(:project).find(type_a_task_ids & type_b_task_ids).map { |task|
    [ task.id, task ]
  }]
# proceed with cache-centric logic
type_a_task_ids = [1,2,3,1,2,3]
type_b_task_ids = [1,2,2,3,3] 
type_a_tasks = type_a_task_ids.map { |task_id| my_tasks_identity_map[task_id] }
type_b_tasks = type_b_task_ids.map { |task_id| my_tasks_identity_map[task_id] }

如果通过config/application.rb中的类似行在Rails中启用IdentityMap:

config.active\u record.identity\u map=true


那么ActiveRecord实际上不会返回到DB来加载它之前已经加载过的项目-它只会引用内存中的同一个对象。

我不太明白-解决方案似乎就是简单地为任务加载项目?即任务。包括:项目。。?也许这个问题需要更多的细节。@vladr我想如果是这样的话,这样的缓存策略可能对任务也很有用,所以他是一个全能的赢家。@jstephenson也许他没有所有的任务可以提前完成@nambrot IIRC即使在重复的SQL查询没有按顺序发生时也能工作。您是否尝试过使用Project.cache{…}显式包装所有查询?。如果运气不好,那么你也可以选择a,但你绝对应该首先尝试让ActiveRecord缓存工作。嘿,伙计们,谢谢你们的建议嘿,伙计们,谢谢你们的建议,@jstepheson:我从其他地方获得了一个巨大的任务列表,因此我需要有效地加载关联,而不是急于加载,对吧?@vladr:查询缓存可以工作,但我有很多任务,所以仍然不够。我想避免使用全局缓存,但我知道这也可以。我只是觉得会有一种更讽刺的方式谢谢你的回答。我试图补充我的问题。本质上,我需要在任务对象上设置项目对象以进行进一步处理。IdentityMap正在从Rails中删除-发布前您是否阅读了其他答案?@vladr如果您在最后阅读了pull请求,他们会得出不会删除的结论,这有待进一步讨论。很抱歉,我确实浏览了其他答案,但我错过了您对IdentityMap的引用。是的,我确实阅读了拉取请求,直到最后,9个月前的删除位置@弗拉德我明白了,是的,你说得对。我通读了拉请求,它似乎没有被删除,因此我感到困惑。那太糟糕了,我喜欢即时通讯功能,从来没有遇到过任何问题,而且我有两个相当复杂的应用在生产中。哦,好吧,我想我们有更多的往返数据库。
type_a_task_ids = [1,2,3,1,2,3]
type_b_task_ids = [1,2,2,3,3] 
type_a_tasks = type_a_task_ids.map { |task_id| Task.includes(:project).find(task_id) }
type_b_tasks = type_b_task_ids.map { |task_id| Task.includes(:project).find(task_id) }
# with IdentityMap turned on (see IdentityMap documentation)
# prefetch all distinct tasks and their associated projects
# throw away the result, we only want to prep the cache
Task.includes(:project).find(type_a_task_ids & type_b_task_ids)
# proceed with regular logic
type_a_task_ids = [1,2,3,1,2,3]
type_b_task_ids = [1,2,2,3,3] 
type_a_tasks = type_a_task_ids.map { |task_id| Task.includes(:project).find(task_id) }
type_b_tasks = type_b_task_ids.map { |task_id| Task.includes(:project).find(task_id) }
# prefetch all distinct tasks and their associated projects
# store the result in our own identity cache
my_tasks_identity_map = \
  Hash[Task.includes(:project).find(type_a_task_ids & type_b_task_ids).map { |task|
    [ task.id, task ]
  }]
# proceed with cache-centric logic
type_a_task_ids = [1,2,3,1,2,3]
type_b_task_ids = [1,2,2,3,3] 
type_a_tasks = type_a_task_ids.map { |task_id| my_tasks_identity_map[task_id] }
type_b_tasks = type_b_task_ids.map { |task_id| my_tasks_identity_map[task_id] }