Update or Push Embedded Document to Array - ruby

I have two document
class Holder
include Mongoid::Document
embeds_many :things
end
class Thing
include Mongoid::Document
embedded_in :holder
end
Is there a way to find if some element exists in Holder.things and updated it or if not create a new one? Similar to the upsert flag in mongoDB.
Thank you.

What have you tried so far? Look into the embedded 1-N relationship functions that Mongoid provides (here).
#myHolder.things.find_or_create_by(attr: 'val')
# or
if #myHolder.things.where(attr: 'val').empty? ...

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

Can I remove an embedded document in Mongoid without persisting?

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.

Mongoid: How to implement a relationship between embedded documents?

I have a situation where I have a parent document and I want to have two different types of embedded documents: one as a parent, and another as a child with an optional parent. For example:
class ParentDoc
include Mongoid::Document
embeds_many :special_docs
embeds_many :special_doc_groupings
end
class SpecialDoc
include Mongoid::Document
embedded_in :parent_doc
belongs_to :special_doc_groupings
end
class SpecialDocGrouping
include Mongoid::Document
embedded_in :parent_doc
has_many :special_docs
end
In this example, SpecialDocs and SpecialDocGroupings can exist without a relationship, or optionally can have a parent-child relationship.
However, this is an invalid Mongoid association because we get this error:
Mongoid::Errors::MixedRelations:
Problem:
Referencing a(n) SpecialDoc document from the SpecialDocGrouping document via a relational association is not allowed since the SpecialDoc is embedded.
Summary:
In order to properly access a(n) SpecialDoc from SpecialDocGrouping the reference would need to go through the root document of SpecialDoc. In a simple case this would require Mongoid to store an extra foreign key for the root, in more complex cases where SpecialDoc is multiple levels deep a key would need to be stored for each parent up the hierarchy.
Resolution:
Consider not embedding SpecialDoc, or do the key storage and access in a custom manner in the application code.
I don't see anything wrong with the type of association that I'm trying to create, besides the fact that it's not supported by Mongoid.
How can I implement this type of association myself?
The association is not valid because when you reference embedded model Mongoid does not store the parent key as foreign key.
This means that if you have:
Class Parent
embeds_many :children
end
Class Child
embedded_in :parent
end
You cannot reference Child document storing only its foreign key, but you need to store all the parents keys until you reach the root.
In this case the root is represented by the first parent and you need to store 2 keys.
You can accomplish this manually, and create this type of association without any problem.
Your case is a bit different (and easier) because you want to create the association between two models embedded in the same parent. That means theoretically you don't need to store the parent key because the models share the same root.
Mongoid does not handle this scenario, so you need to manually create your association rules, and methods.
Class Bar
embeds_many :beers
embeds_many :glasses
end
Class Beer
embedded_in :bar
# Manual has_many :glasses association
def parent
self.bar
end
def glasses
parent.glasses.where(:beer_id => self.id)
end
end
Class Glass
embedded_in :bar
# Manual belongs_to :beer association
field :beer_id, type: Moped::BSON::ObjectId
def parent
self.bar
end
def beer
parent.beers.find(self.beer_id)
end
end
(the code is not tested)

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 do I query for subclasses in embedded documents with mongoid?

I have a Box, with many toys of various types:
class Box
embeds_many :toys
end
class Toy
field :name
embedded_in :box
end
class Car < Toy
end
class Doll < Toy
end
If I have found the specific box object I want, how do I query it to find just the cars?
my_box.toys.where(??? Car ??? )
Obviously I could iterate over each toy and extract the ones where toy.is_a? Car, but I'm looking for a solution, if one exists, that just uses the built in mongoid criteria/finders.
Thats a good question. luckily Mongoid used to store _type meta attribute in the document to handle these kind of scenarios
so you can find the cars in the box by
Box.where('toys._type'=>'Car')
Thats all

Resources