更新内存中急切加载的关联

发布于 2024-07-17 01:16:46 字数 2044 浏览 5 评论 0原文

在我当前的 Rails 应用程序中,我将大量数据从数据库提取到内存中,以便我可以启动一个长时间运行的后台任务,我不想每 5 秒访问一次数据库。

这一切都很好,但我有一些不优雅的代码,如果可能的话我想删除它们。

例如,我有一个用户模型,并且该用户模型可以以联系人的形式链接到其他用户模型,而联系人又可以链接回第一个用户模型,依此类推。 建模如下(感谢 Milan Novota 之前的帮助): -

class User < ActiveRecord::Base
  has_many :contact_records, :foreign_key => :owner_id
  has_many :contacts, :through => :contact_records, :class_name => "User"
end

class ContactRecord < ActiveRecord::Base
  belongs_to :owner, :class_name => "User"
  belongs_to :user
end

现在我可以输入

user_a_contacts = user_a.contact_records
=> [user_b, user_c]

并获取用户 a 的所有联系人。 现在假设我想通过用户 a 获取用户 b 的联系人(无法想象为什么我会这样做,但这只是一个例子!)

user_b_contacts = user_a_contacts.first.contact_records
=> [user_a, user_c]

现在我可以获取用户 a 的姓名(以一种复杂的、迂回的、从不使用的方式)现实世界的方式)

user_a_name = user_b_contacts.first.name
=> "Jamie"

到目前为止,一切都很好。 现在我更改用户 a 的名称

user_a.name = "Sandy"
=> "Sandy"

如果像以前一样从数据库中再次访问用户 a 的名称,我会得到

user_a_name = user_b_contacts.first.name
=> "Sandy"

但是,如果我已经急切加载并将该值保留在内存中,我会得到

user_a_name = user_b_contacts.first.name
=> "Jamie"

但执行以下操作给了我正确的答案

user_a.name
=> "Sandy"

很明显,急切的-加载正在为原始用户和从 ContactRecord 对象中急切加载的用户创建不同的 User 对象。

所以(最后!)我的问题是这样的: -

目前,我已经通过使用

@users.each.detect{|user| user == user_b_contacts.first}

(其中 @users 是急切加载的 User 对象列表)来解决这个问题。 有没有办法可以更新急切加载的 User 对象?

请注意:-a

)我正在使用以下内容进行急切加载

User.find(:all, :include => [ {:contact_records => :contacts] } ])

b)显然这不是我正在使用的实际代码,但为了显示该代码会有更多的解释和说明,我想我已经够烦你了! 相信我,我使用的真实示例确实需要这种结构才能正常工作,尽管看起来很奇怪! ;)

c) 我在重建示例时可能引入了拼写错误和错误,但请忽略这些。 我需要知道的是是否可以在不访问数据库的情况下重新加载急切的用户。

提前谢谢了!

更新:如下所述,直到进程运行结束时模型数据才会保存到数据库中,因此重新加载将不起作用,因为数据库中的数据不会更改。

In my current rails app I'm pulling a lot of data from the database into memory so that I can start a long-running background task that I don't want to hit the database every 5 seconds.

This is all working great but I have some inelegant code that I'd like to get rid of if possible.

For an example, I have the situation where I have a User model and that User model can link to other User models in the form of Contacts, which in turn can link back to the first User model an so on. This is modelled as follows (thanks to Milan Novota for his earlier help): -

class User < ActiveRecord::Base
  has_many :contact_records, :foreign_key => :owner_id
  has_many :contacts, :through => :contact_records, :class_name => "User"
end

class ContactRecord < ActiveRecord::Base
  belongs_to :owner, :class_name => "User"
  belongs_to :user
end

So now I can type

user_a_contacts = user_a.contact_records
=> [user_b, user_c]

and get all the contacts for user a. Now let's say I want to get user b's contacts via user a's (can't think why I would but it's only an example!)

user_b_contacts = user_a_contacts.first.contact_records
=> [user_a, user_c]

Now I can get user a's name (in a convoluted, roundabout, never-use-in-the-real-world kind of way)

user_a_name = user_b_contacts.first.name
=> "Jamie"

So far, so good. Now I change user a's name

user_a.name = "Sandy"
=> "Sandy"

If accessing user a's name again as before from the database I get

user_a_name = user_b_contacts.first.name
=> "Sandy"

However, if I've eager loaded and kept this value in memory I get

user_a_name = user_b_contacts.first.name
=> "Jamie"

but doing the following gives me the right answer

user_a.name
=> "Sandy"

Fairly obviously, the eager-loading is creating different User objects for the original users and those eager-loaded from the ContactRecord objects.

So (finally!) my question is this: -

At the moment, I have bodged around this by using

@users.each.detect{|user| user == user_b_contacts.first}

(where @users is the eager-loaded list of User objects). Is there a way that I can update the eager-loaded User objects instead?

Please note: -

a) I am eager-loading using the following

User.find(:all, :include => [ {:contact_records => :contacts] } ])

b) Obviously this is not the actual code that I am working with but there would have been even more explanation and exposition in order to show that code and I think I've bored you enough! Trust me that the real example I am using DOES require this structure in order to work correctly, as bizarre as it might look! ;)

c) I may have introduced typos and errors when reconstructing the example but please ignore those. All I need to know is whether it is possible to reload the eager-loded Users without hitting the database.

Many thanks in advance!

UPDATE: As mentioned below, the model data will not be saved to the database until the very end of the process run so reloading will not work as the data in the database won't have changed.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

小…红帽 2024-07-24 01:16:47

非常感谢您的回复,丹尼尔。 虽然你没有提出正确的问题,但你让我意识到我错过了一个关键点! 数据库记录不会更新,因为我没有进行保存! 在任何时候。

我所做的要点是将模型数据提取到实例变量中,以加快后台进程。 因此,第一次加载的数据直到进程运行结束时才会被保存回数据库。

我所希望的是一种在急切加载时可以指定一列的值与已加载的另一个模型相关的方法。

例如,

User.find(:all, :include => [ {:contact_records => (:contacts as User)] } ])

或者类似的。

据我所知,Rails 中没有这样的语法,但我希望我错了! :)

希望这能让它更清楚一点,并再次感谢您的回复。

Many thanks for your reply, Daniel. Whilst you've not quite got the right question you have made me realise that I missed out a crucial point! The database record will not have been updated as I'm not doing a save! at any point.

The gist of what I'm doing is pulling model data out into instance variables in order to speed up a background process. As such, the data that is loaded the first time round will not be saved back to the database until the very end of process run.

What I was hoping for was a way that you could specify when eager-loading that the value of one column relates to another model already loaded.

For instance,

User.find(:all, :include => [ {:contact_records => (:contacts as User)] } ])

or similar.

As far as I am aware there is no such syntax in Rails, however I'm hoping that I'm wrong! :)

Hope this makes it a bit clearer and thanks again for the reply.

美胚控场 2024-07-24 01:16:46

好的。 我将把你的问题压缩成一个新问题并回答这个问题。 如果我误解了,请原谅我,这不是您要问的。

问题

如果user是一个ActiveRecord模型,我已经提前使用User.first(:include => :contacts)<加载了关联contacts /code>,当数据库中的 contacts 关联发生更改时,如何刷新它?


回答

据我所知,您可以通过两种方式使关联缓存失效。 首先你可以这样做:

user.reload

这将从头开始重新加载整个用户模型。 一种稍微不那么激烈的方法是,

user.clear_association_cache

不会重新加载所有用户,但会清除缓存的关联。

无论哪种方式,下次您执行 user.contacts 时,它都会从数据库中重新加载它们。

OK. I'm going to condense your question into a new question and answer that one. Forgive me if I've misunderstood and this is not what you are asking.

Question

If user is an ActiveRecord model where I have loaded an association contacts ahead of time with User.first(:include => :contacts), how do I refresh the contacts association when it has changed in the database?

Answer

You can invalidate the association cache in two ways that I know of. First you can do:

user.reload

which will reload the entire user model from scratch. A slightly less drastic way to do this is to do

user.clear_association_cache

which will not reload all of user, but will clear the cached associations.

Either way, the next time you do user.contacts it will load them afresh from the database.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文