vdaubry
vdaubry

Reputation: 11439

Mongoid : Embedded documents are saved under the wrong parent

When saving documents with 3 levels of nesting, child objects are saved under the wrong parent :

user = User.create
website = user.websites.create
post = website.posts.create
post2 = website.posts.create

post.images.create
post2.images.create

puts "#{user.to_json}"
puts "#{user.reload.to_json}"

Each post should have an image, that's true on the dirty user object (user.to_json) => https://gist.github.com/vdaubry/cdc465d6d5ef84576830

But when i reload the user all images are embedded under the first post (user.reload.to_json) => https://gist.github.com/vdaubry/a9c217a467dd9ff9a7fb

Is it a bug or i am missing something obvious ?

Here are the class definition used to reproduce this :

class User
  include Mongoid::Document
  embeds_many :websites
end

class Website
  include Mongoid::Document
  include Mongoid::Timestamps
  embedded_in :user
  embeds_many :posts

end

class Post
  include Mongoid::Document
  include Mongoid::Timestamps

  embedded_in :website
  embeds_many :images
end
class Image
  include Mongoid::Document
  include Mongoid::Timestamps
  embedded_in :post
end

My gemfile :

ruby 2.1.2p95
gem 'rails', '~> 4.1.4'
gem 'mongoid', '~> 4.0.0'

Upvotes: 2

Views: 973

Answers (2)

vdaubry
vdaubry

Reputation: 11439

Thanks a lot for your answer ! For those having the same problem it's a limitation of mongo which doesn't support multiple embedded levels of collections.

I refactored my model to only embed 2 level of collection. It works with :

class User
  include Mongoid::Document
  has_many :websites
end

class Website
  belongs_to :user
  embeds_many :posts
end

class Post
  embedded_in :website
  embeds_many :images
end

class Image
  embedded_in :post
end

I guess the best way to get this working is upvoting the underlying issue directly on the mongo jira :

https://jira.mongodb.org/browse/SERVER-831

Upvotes: 0

Ahmad Sherif
Ahmad Sherif

Reputation: 6223

It's a bug, I'm not sure on which end the bug exists, Mongoid or MongoDB. It happens because Mongoid replaces an index in the key of the field we're pushing to with the positional operator "$", i.e. "websites.0.posts.1.images" is changed to "websites.0.posts.$.images".

Quoting the docs on the positional operator (italics are mine):

When used with the update() method,

  • the positional $ operator acts as a placeholder for the first element that matches the query document,

In your case, image documents are inserted into the first post document created, so this could be the reason.

I'm not sure why Mongoid does that (replacing with positional operator) or is it necessary for a $push operation.

I see you already created an issue for them, so I'm copying my comments over there too.

Upvotes: 1

Related Questions