How to build a json object of a Mongoid object with children? - ruby

I have two simple classes
class Band
include Mongoid::Document
field :name, type:String
has_many :members
end
class Member
include Mongoid::Document
field :name, type: String
belongs_to :band
end
After I have created two object for test purposes
Band.create(title: 'New Band')
Band.members.create(name: 'New Member')
I got next db state:
> db.bands.find()
{ "_id" : ObjectId("..."), "title" : "New Band" }
> db.members.find()
{ "_id" : ObjectId("..."), "name" : "New Member", "band_id" : ObjectId("...") }
When I try to build json object of Band object I get data without children:
{"_id":"...","title":"New Band"}
But I need something like that:
{"_id":"...","title":"New Band", "members" : {"_id":"...","title":"New Member"}}
How to build json with children??

You can override serializable_hash:
class Member
include Mongoid::Document
field :name, type: String
belongs_to :band
def serializable_hash(options={})
{
id: id,
name: name
}
end
end
class Band
include Mongoid::Document
field :title, type: String
has_many :members
def serializable_hash(options={})
{
id: id,
title: title,
members: members.inject([]) { |acc, m| acc << m.serializable_hash; acc }
}
end
end
Suppose you have a band with a member:
band = Band.create(title: 'New Band')
band.members.create(name: 'New Member')
In that case band.to_json will return you something like that:
"{\"id\":...,\"title\":\"New Band\",\"members\":[{\"id\":...,\"name\":\"New Member\"}]}"

Try this:
a_band = Band.last
a_band.as_json(methods: [:members])
Mongoid auto-generates helper methods for your relations, and you can include these methods when you build your JSON object. You can use a_band.members to fetch the band's members out of the db, so you can include that method in your JSON object, like any other method on the model.

Related

Create a foreign key using belongs_to between two mongoid collections

There are two classes.
First class
class Baby
include Mongoid::Document
field :first_name, type: String
field :surname, type: String
field :dob, type: Integer
belongs_to :parent, foreign_key: :username
validates :parent, :presence => true
end
Second class
class Parent
include Mongoid::Document
field :username, type: String
field :first_name, type: String
field :surname, type: String
field :email, type: String
field :password, type: String
has_many :baby
end
When I create a new instance of the Baby class, I pass in a username. This should correlate to a username in the Parent document. Effectively storing a relation between the Baby and the Parent.
Baby.create!(first_name: 'James', surname: 'Barr', dob: '17-09-2012', username: 'foo123')
I keep getting this every time:
Mongoid::Errors::Validations:
message:
Validation of Baby failed.
summary:
The following errors were found: Parent can't be blank, Parent can't be blank
resolution:
Try persisting the document with valid data or remove the validations.

Unable to create correct Mongoid model from JSON

I'm trying to create a Mongoid model from the corresponding JSON structure.
However it does not include the embedded relation frameworks.
I'm using Mongoid 4.0. Am I doing something wrong or is this a bug?
If I store any embedded relation via store_as under a different name than the default serialization, it works as expected. Also, if I create the model in the database from JSON rather than initialize it everything's fine...
JSON input
{
"name": "MyName",
"frameworks": [
{
"name": "grails",
"runtime": "groovy",
"versions": []
}
]
}
Models
require 'mongoid'
class Vendor
include Mongoid::Document
include Mongoid::Attributes::Dynamic
# fields
field :name, type: String
# relations
embeds_many :frameworks
# validations
validates :name, presence: true
validates :frameworks, presence: true
end
class Framework
include Mongoid::Document
embedded_in :vendor
field :name, type: String
field :runtime, type: String
field :versions, type: Array
# validations
validates :name, presence: true
validates :runtime, presence: true
end
Test App
require 'json'
require 'require_relative'
require_relative 'vendor'
begin
json = JSON.parse(File.read('input.json'))
#profile = Vendor.new(json)
puts #profile.inspect
rescue JSON::ParserError => e
puts "Error: " << e.to_s
end

Mongoid nested embedded document save ignores query

I'm having trouble saving the correct nested embedded document with mongoid. I've tried this in both v3.1.6 and v4.0.0 and the result is the same. The parent document is saved, but the nested document I modified ignores the changes, and instead updates the first nested document instead.
Assume some models like so:
Mongoid.configure do |config|
config.sessions = {default: {hosts: ["localhost:27017"], database: "test_mongoid"}}
end
class Band
include Mongoid::Document
field :name, type: String
embeds_many :members
end
class Member
include Mongoid::Document
embedded_in :band
field :name, type: String
embeds_many :instruments
end
class Instrument
include Mongoid::Document
embedded_in :member
field :name, type: String
def make_badass!
self.name = "Badass #{self.name}"
self.save
end
end
And a program that runs:
Band.destroy_all
a_band = {
name: "The Beatles",
members: [
{
name: 'Paul',
instruments: [
{
name: 'Bass guitar'
},
{
name: 'Voice'
}
]
}
]
}
Band.create(a_band)
the_beatles = Band.first
puts the_beatles.as_document
paul = the_beatles.members.first
voice = paul.instruments.where({name: "Voice"}).first
voice.make_badass!
puts Band.first.as_json
The database should now contain:
{
"_id": ObjectId('53aa7d966d6172889c000000'),
"name" : "The Beatles",
"members" : [
{
"_id" : ObjectId('53aa7d966d6172889c010000'),
"name" : "Paul",
"instruments" : [
{"_id" : ObjectId('53aa7d966d6172889c020000'), "name" : "Bass guitar"},
{"_id" : ObjectId('53aa7d966d6172889c030000'), "name" : "Voice"}
]
}
]
}
But instead, it contains:
{
"_id": ObjectId('53aa7d966d6172889c000000'),
"name" : "The Beatles",
"members" : [
{
"_id" : ObjectId('53aa7d966d6172889c010000'),
"name" : "Paul",
"instruments" : [
{"_id" : ObjectId('53aa7d966d6172889c020000'), "name" : "Badass Voice"},
{"_id" : ObjectId('53aa7d966d6172889c030000'), "name" : "Voice"}
]
}
]
}
What would be a working way of having the correct embedded document to change from within the instance method of Instrument?
Thanks for your help!
Mongoid is quickly forcing me to become an alcoholic. Hopefully it will help someone in the same situation.
class Instrument
include Mongoid::Document
embedded_in :member
field :name, type: String
def make_badass
self.name = "Badass #{self.name}"
self.member.band.save
end
def unset_name
# self.unset :name does not work
self.remove_attribute :name
self.member.band.save
end
end

Creating Mongoid embedded documents

I am using Mongoid for the first time. I want to store a collection of emails that have a subject, body, and arrays of to, cc, and bcc recipients. Example:
{to: [{email: 'andrew#example.com', name: 'Andrew'}], cc: ...
However, I can't seem to figure out how to model this data using Mongoid. I think the term for these are called embedded documents, but everything I've tried does not seem to work right. How do I create my models correctly with Mongoid?
Here is the solution. You can specify class name if you want to reuse a class for multiple fields:
class Email
include Mongoid::Document
embeds_many :to_recipients, :class_name => "Recipient"
embeds_many :cc_recipients, :class_name => "Recipient"
embeds_many :bcc_recipients, :class_name => "Recipient"
embeds_one :from, :class_name => "Recipient"
field :subject, type: String
field :body_text, type: String
field :body_html, type: String
end
class Recipient
include Mongoid::Document
field :email_address, type: String
field :name, type: String
validates :email_address, :presence => true
embedded_in :emails
end

Mongoid - getting all attributes including embedded documents

Is there an easy way to get all attributes of a Mongoid document, including those of embedded documents?
For example, if I have the following documents:
class Person
include Mongoid::Document
embeds_many :phone_numbers
field :name
end
class PhoneNumner
include Mongoid::Document
embedded_in :person, :inverse_of => :phone_numbers
field :number
end
I would like to get a Person's attributes and phone numbers like this:
{ :name => "Jenny", :phone_numbers => [{ :number => '867-5309' }, { :number => '867-5309' }] }
Since embedded documents are really just other attributes on the parent document, you can get to them like so:
person = Person.create
person.phone_numbers.create(:number => "123-456-7890")
person.attributes
# => {"_id"=>"4c48ff26f7e2da3704000001",
# "phone_numbers"=>
# [{"number"=>"123-456-7890", "_id"=>"4c48ff26f7e2da3704000002"}]}

Resources