Big confusion on my part with embedded documents, please help !

81 views
Skip to first unread message

Alexandre Ponsin

unread,
Apr 11, 2011, 12:29:33 AM4/11/11
to Mongoid
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

Alexandre Ponsin

unread,
Apr 11, 2011, 12:34:03 AM4/11/11
to Mongoid
ps: I am using rails 3.0.3 and mongoid 2.0.1

On Apr 11, 12:29 pm, Alexandre Ponsin <alexandre.pon...@gmail.com>
wrote:

Rubish Gupta

unread,
Apr 11, 2011, 2:57:28 AM4/11/11
to mon...@googlegroups.com, Alexandre Ponsin
I don't get why would you need to do

localized_content.update_attributes(params[:localized_content])

You are already accepting nested attributes for localized_content in content. I have a similar setup and didn't face any such problems. My forms have structure like:

form_for @content do |f|
  f.fields_for :localized_contents do |ff|
    ff.text_field :blah
    # blah blah blah
  end
end

in my controller i simply do:

@content = Content.find(params[:id])
@content.update_attributes(params[:content])

It correctly saves the localized contents and updates timestamps too. However, I am not using versioning, so can't comment on that.

However, if your form structure differs, you can try this:

@content = Content.where("localized_contents._id" => params[:localized_content][:id]).first
@content.update_attributes(params[:content])

Hope it helps.

--
Rubish

Alexandre Ponsin

unread,
Apr 11, 2011, 11:23:12 AM4/11/11
to Mongoid
Hi,

Thank you for your reply, but actually, I am not using the nested
attributes for this particular model.
So I should remove them I guess...

But even tough I was, how would you perform atomic updates on "just"
the localized_content and save it without getting the dreaded error
msg ?

Thanks,

Alex

Rubish Gupta

unread,
Apr 11, 2011, 1:59:54 PM4/11/11
to mon...@googlegroups.com, Alexandre Ponsin
I guess following should work:

@content = Content.where("localized_contents._id" => params[:localized_content][:id]).first
@content.localized_contents.where("_id" => params[:localized_content][:id]).first.update_attributes(params[:localized_content])

It doesn't look elegant though. Probably there is a better way to do this.

--
Rubish

Alexandre Ponsin

unread,
Apr 12, 2011, 1:46:21 AM4/12/11
to Mongoid
Nope sorry, I had tried that already... I got the error message
telling me to access via the root :(

Alex

Alexandre Ponsin

unread,
Apr 13, 2011, 11:04:27 PM4/13/11
to Mongoid
Any more ideas ? I am still not sorted out with this ?
I can't believe no one knows how to atomically update an embedded
document ?

Alex

On Apr 12, 1:46 pm, Alexandre Ponsin <alexandre.pon...@gmail.com>

Rubish Gupta

unread,
Apr 14, 2011, 1:54:15 AM4/14/11
to mon...@googlegroups.com, Alexandre Ponsin
Hi Alex,

I tested it in rails console, as I was pretty much sure that it was working fine for me. It seems the problem is with Paranoia and Versioning, may be they are not designed to be used by embedded documents.

Code I used to test(enclosed between "===========" lines)

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

  embeds_many :localized_contents

  field :name, :type => String
end

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

  embedded_in :content, :inverse_of => :localized_contents

  field :locale, :type => String
  field :name, :type => String
end

c = Content.create(:name => "Test")

# Mongoid::Errors::InvalidCollection if versioning
c.localized_contents << LocalizedContent.new(:locale => "en", :name => "Test en", :_id => "111111111111111111110001") 

# wait 4-5 secs before executing this to 
# actually note that updated_at is updating properly
c.localized_contents.find("111111111111111111110001").update_attributes(:locale => "en_IN") 

c.reload

# Mongoid::Errors::InvalidCollection if paranoia
c.localized_contents.first.delete 
=============================================================
Reply all
Reply to author
Forward
0 new messages