Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mongoid, confused with embedded document with timestamps and versioning?

I have been using Mongoid for about 3 months now, and I have managed to get done pretty much anything I need thanks to the great document and resources out there.

But going back to improve some stuff I have made a few backs, I am definitely struggling a lot on embedded documents.

In a nutshell what I am trying to do, is to maintain versioning and timestamps on embedded documents, but that I cannot manage to do.

Here is the relevant part of my model:

class Content
  include Mongoid::Document
  include Mongoid::Timestamps
  include Mongoid::Paranoia

  embeds_many :localized_contents
  accepts_nested_attributes_for :localized_contents
end

class LocalizedContent
  include Mongoid::Document
  include Mongoid::Timestamps
  include Mongoid::Paranoia
  include Mongoid::Versioning

  embedded_in :content, :inverse_of => :localized_contents
end

Nothing really complicated here, everything works fine regarding the behavior of the Content model, however the LocalizedContent model is not behaving the way I am expecting to, so my expectations either needs to get straighten up, or I need help fixing what I am doing wrong.

To create a new embedded document I do the following:

my_content = Content.find(params[:id])
my_content.localized_contents.build(params[:localized_content])
if parent.save
 #redirect, etc.
end

This works in the sense that it successfully creates a new embedded document in the correct Content, however the timestamps fields I left a nil

Now, if I try to update that localized_content:

my_content = Content.find(params[:content_id])
localized_content = my_content.localized_contents.find(params[:id])

Now, if I do: localized_content.update_attributes(params[:localized_content]) I get the following error:

=> Mongoid::Errors::InvalidCollection: Access to the collection for LocalizedContent is not allowed since it is an embedded document, please access a collection from the root document.

Fair enough, then I update atomically the fields on the localized content and save the parent:

localized_content.fieldA = "value"
localized_content.fieldB = "value"
localized_content.fieldC = "value"

my_content.save

This works in updating the localized content properly but: - timesteamps (udpated_at and created_at) are still nil - versions does not receive the a copy of the current localized_content and version does not get incremented !

So as I read in many occasion in this groups and on some forums on the web, the call backs are not triggered on the embedded document for performance reason, since I am calling save on the parent. Again, faire enough, but as suggested in those places, I should call save on the embedded docs instead... but how !?!?! because every time I do I get the dreaded:

=> Mongoid::Errors::InvalidCollection: Access to the collection for LocalizedContent is not allowed since it is an embedded document, please access a collection from the root document.

Even more so, I tried to manually call the call back for versioning on my embedded: localized_content.revise, and again same error:

=> Mongoid::Errors::InvalidCollection: Access to the collection for LocalizedContent is not allowed since it is an embedded document, please access a collection from the root document.

I am going nuts here ! Please help. What I am doing wrong ? How should an embedded document be create and updated so I can call (even manually I don't care) the proper callbacks to update the time stamps and versioning ?

Thanks,

Alex

ps: I am using rails 3.0.3 and mongoid 2.0.1

like image 538
Alex Avatar asked Dec 02 '22 02:12

Alex


2 Answers

Just in case this answer is still useful to anyone, Mongoid has added a tag which makes callbacks run on embedded child objects when the parent object is saved.

Your parent object should now look like this:

class Content
  include Mongoid::Document
  include Mongoid::Timestamps
  include Mongoid::Paranoia

  embeds_many :localized_contents, cascade_callbacks: true
  accepts_nested_attributes_for :localized_contents
end

That's it! Now, saving the parent object will run callbacks on the child objects (and Mongoid::Timestamps is smart enough to only run on the objects which were actually changed). This information is in the mongoid documentation, at the very bottom of the embedded documents page.

like image 180
talon55 Avatar answered Dec 21 '22 16:12

talon55


Try using create instead of build. EmbeddedDoc.build and EmbeddedDoc.new won't fire any save callbacks (because nothing's being saved yet), and saving the parent doc won't call the embedded children's callbacks (performance decision). EmbeddedDoc.create should fire the embedded docs callbacks though.

my_content = Content.find(params[:id])
puts my_content.localized_contents.create(params[:localized_content])
like image 25
Dave Rapin Avatar answered Dec 21 '22 14:12

Dave Rapin