Can I remove an embedded document in Mongoid without persisting? - ruby

Definitely related to this question, but since there was no clear answer, I feel like I should ask again. Is there any way to remove an embedded document from a Mongoid embeds_many relationship, without persisting?
I want to modify the array of embedded documents in-memory - and then persist all changes with a single UPDATE operation. Specifically, I'd like to:
Modify arrays of embedded documents (add embedded doc / remove embedded doc / edit embedded doc / etc).
Possibly make other changes to the TLD.
Persist all changes with a single database call.

It is possible to remove an embedded document using Mongoid without saving. The trick is to make your changes from the parent object using assign_attributes. For exmaple:
class MyParent
include Mongoid::Document
field :name, type: String
embeds_many :my_children
def remove_my_child(child)
assign_attributes(my_children: my_children.select { |c| c != child })
end
end
class MyChild
include Mongoid::Document
embedded_in :my_parent
def remove
parent.remove_my_child(self)
end
end
my_parent = MyParent.first
my_first_child = my_parent.my_children.first
# no mongo queries are executed
my_first_child.remove
# now we can make another change with no query executed
my_parent.name = 'foo'
# and finally we can save the whole thing in one query which is the
# reason we used an embedded document in the first place, right?
my_parent.save!

After two more years of using Mongoid, I've learned there's no operator for what I was trying to achieve. Removing an embedded document with Mongoid always results in a database call.
In situations like this one, it's easier to bypass Mongoid and use the mongo-ruby-driver directly.

Try mongoid's
update_all()
Documentation
Ex: If I wanted to make all my users Joe
User.update_all(name: 'Joe')
will behave exactly as you would expect.

Related

Where can I find a specific documentation for a Mongoid class?

I am new to Mongoid and I want to learn more about it. Today I came across Mongoid::Timestamps in here .
It looks interesting to me so I wanted to know more about it, but could not find any more documentation on Mongoid::Timestamps. I've currently tried searching here, in Class List, Method List and File List, but didn't find anything.
Seems like Mongoid has a very limited documentation.
You can read its RubyDoc to find more about Mongoid::Timestamps. It's nothing special, but in a nutshell:
Including Mongoid::Timestamps in your Ruby model classes would tell MongoDB to save timestamps of when a document was created or updated. This creates two new attributes in your model:
created_at
updated_at
You use it like this:
class User
include Mongoid::Document
include Mongoid::Timestamps
# Other stuff...
end

save Hash to a column (type: text) in database by serialization, why is it saved YAML

We are trying to serialize an attribute called differences in order to save the value of this attribute as a Hash into database. The type of differences is text. it used to be saved as Hash in the database. But recently we found out it is saved yaml. is there any way to prevent it saving as yaml?
Class A < ActiveRecord::Base
has_many :bs
serialize :differences
def diff(other)
b_diffs = {}
bs.zip(other.bs).each do |a,b|
b_diffs.merge!(:bs => a.diff?(b)
end
end
def diff_against_last_a
last_a = ....
self.differences = last_a.diff(self)
end
end
Any suggestion?
EDIT:
my problem is i can't retrieve differences as Hash.
Interestingly, when i check on console, it is saved as yaml, and retrieved as yaml. and then i rerun the the method diff(other) and save the result to differeces, it shows as Hash and i can retrieve Hash. But it doesn;t work this way when running on my app.
I think the problem here is the serialized data are no longer returned Hash.
UPDATE:
So i found out under the development environment, there is such a problem. But in production and staging (we use heroku), it practises fine. so We'll consider it ok then.
In addition to my comment, here is the official description:
If you have an attribute that needs to be saved to the database as an
object, and retrieved as the same object, then specify the name of
that attribute using this method and it will be handled automatically.
The serialization is done through YAML. If class_name is specified,
the serialized object must be of that class on retrieval or
SerializationTypeMismatch will be raised.
source: http://apidock.com/rails/ActiveRecord/Base/serialize/class
In layman's terms "When Rails serializes a hash to save in the db, all it does is convert it to YAML so that it can be stored as a string."
As the last line of official description reads, you have define the class of retrieval. Hash in your case. So declare serialize like this:
serialize :differences, Hash

cascade_callbacks not working for embedded doc in mongoid

I'm using Mongoid, and I have one class (Order) that "embeds_many" of another class (LineItem). Specifically, in Order, I have:
embeds_many :line_items, cascade_callbacks: true
And in LineItem, I have:
embedded_in :order
Everything works as it should except for the cascading_callbacks. When I make a change to an Order object and call "save", I would expect the "after_save" callbacks on all of the embedded LineItem objects to be called. This is not happening. I thought that was the point of cascading_callbacks.
Am I incorrect? Is something else required to make the save-related callbacks fire for each embedded document when the parent is saved and cascade_callbacks is set to true in the relationship?
I think, when you specify cascade callbacks: true in the parent document, the callbacks of the embedded documents are fired only when you make any change in the corresponding embedded document.
To check, change something in the embedded document and save the parent document. In this case I think the callbacks will be fired

Mongoid and Full_text search issue

I have been working on rails project, which had been made using noSql(Mongoid). Every thing is running fine.The issue is I want to add.. full text search here also. I had been using this gems for this...
gem 'mongoid_fulltext'
and my model file looks like this..
class Keyword
include Mongoid::Document
include Mongoid::FullTextSearch
field :name, type:String
#index :name, unique: true
embeds_many :posts
validates_presence_of :name
validates_uniqueness_of :name
fulltext_search_in :name, :index_name => 'name_index'
end
and in controller.
#keywords = Keyword.fulltext_search(params[:search], :index => 'name_index')
and then #keywords always returns an empty array always.
Thanks
Awieet
Apart from what I'm assuming are formatting errors, the only mistake I can find is that you seem to be naming the index manually.
Maybe in your fulltext_search call you should use :index_name => 'name_index' instead of :index => 'name_index'.
I'd advise just not messing with the default name of the index though, and removing that argument entirely from the method call.
Also, were the records persisted before you added the mongoid_fulltext gem? If so you'll need to call the update_ngram_index method on the Class object (or each instance) to add them to the index.
Other than that, have you checked out the mongoid_search gem as an alternative to mongoid_fulltext?
https://github.com/mauriciozaffari/mongoid_search
I've tried both and find find that this one has a much cleaner implementation and interface. Then again, I only use fulltext search sparingly. You may be using fulltext search more than I, and I'm not sure what the differences are feature-wise, but worth a look.
Hope that helps.

How to get activerecord associations via reflection

For normal columns, you can get at them via the columns class method. However, associations may be named something quite different if the foreign_key option is set in the relationship method. For example, given
class Post
has_many :comments, :foreign_key => :message_id # this is a contrived example
end
if I did Post.column_names I could get at message_id, but is there any way to get comments?
Model.reflections gives information about a model's associations. It is a Hash keyed on the association name. e.g.
Post.reflections.keys # => ["comments"]
Here is an example of some of the information it can be used to access:
Post.reflections["comments"].table_name # => "comments"
Post.reflections["comments"].macro # => :has_many
Post.reflections["comments"].foreign_key # => "message_id"
Note: this answer has been updated to cover Rails 4.2 based on MCB's answer and the comments below. In earlier versions of Rails the reflection's foreign_key was accessed using primary_key_name instead, and the keys for the reflections may be symbols instead of strings depending on how the association was defined e.g. :comments instead of "comments".
For future Googlers in Rails 4 the answer would now be:
Post.reflections[:comments].foreign_key # => "message_id"
Taken from here: https://stackoverflow.com/a/15364743/2167965
EDIT:
reflections, as of 4.2, now takes strings instead of symbols which is a fun bug to track down. If you want to keep using symbols you should switch to reflect_on_association(:assoc_name). Also note reflections are actually the public api which will keep reporting things like HABTM, even though it's all has many through under the hood. The reflections Rails is actually using are now in _reflections
For an ActiveRecord object I use:
object._reflections
So, I can manipulate the Hash returned. For instance:
object._reflections.keys.each do |key|
object.public_send(key).destroy_all
end
The above example delete all the relationships from database.

Resources