Reputation: 11439
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
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
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