Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mongoid embeds_many: push document without save in order to preserve dirty state

Tags:

mongoid

In Mongoid, pushing a document into an embeds_many relation automatically persists the document to the database. Normally, this is fine, but I run into problems when I need to track changes to the embedded document.

Say you have two models:

class List
  include Mongoid::Document
  embeds_many :items
  field :title
end

class Item
  include Mongoid::Document
  embedded_in :list
  field :name
end

This happens to the .changes attribute:

list = List.new(title: 'List title')
list.save  #list is now persisted
item = Item.new(name: 'Item name')
item.changes  #returns Hash with {'name' => [nil, 'Item name']}
list.items << item  #saves item to database under the hood
item.changes  #returns empty Hash, because item was autosaved with list

I could use item.previous_changes to inspect the changes that were made before pushing the item into the list, but in my specific case, this would give me all kinds of troubles to keep things manageable.

What I would like to achieve, is to be able to initialize an Item document and then add it to list (via << or push) without persisting it immediately.

I'm aware that Mongoid does provide an option to set up embeds_many relations without persisting (see http://mongoid.org/en/mongoid/docs/relations.html#embeds_many):

list.items.build(name: 'Another item')

The problem there is that Mongoid creates the Item instance for you. In my case, the documents in the embeds_many relation may be subclasses of Item (e.g. SpecialItem < Item), which wouldn't work well with build. But if anyone knows of a way to get around this limitation, I'd also be happy to accept it as an answer.

like image 873
florish Avatar asked Mar 20 '13 11:03

florish


People also ask

How to push data in a MongoDB document?

- GeeksforGeeks How to push data in a MongoDB document ? The insertOne () and insertMany () are the two methods of the MongoDB module in node.js that are used to push the documents in the collections of the MongoDB database.

How are embedded documents stored in Mongoid?

Documents that are embedded using the embeds_one macro are stored as a hash inside the parent in the parent's database collection. You can optionally tell Mongoid to store the embedded document in a different attribute other than the name, by providing a :store_as option.

What is a relation object in Mongoid?

All relation objects in Mongoid are proxies to the actual document or documents themselves, which provide extra functionality for accessing, replacing, appending and persisting.

How many levels can a document be nested in MongoDB?

In MongoDB, you can only nest document up to 100 levels. The overall document size must not exceed 16 MB. Creating Embedded Documents – In MongoDB, you can easily embed a document inside another document.


2 Answers

To answer my own question: the problem is solved by assigning the parent document to the child, instead of adding the child to the list of children.

Continuing on the example above, you should do

item.list = list  #no database query

instead of

list.items << item  #automatic database insert

to set the parent - child reference without autosaving anything to the database.

like image 153
florish Avatar answered Oct 12 '22 19:10

florish


To follow up on the "building a subclass" issue using your example, you can:

list.items.build({
  name: "Another Item"
}, SpecialItem)

To specify the (sub)class that you want Mongoid to build for you.

like image 36
Tapio Saarinen Avatar answered Oct 12 '22 21:10

Tapio Saarinen