How to save embeds_many relation in Mongoid? - ruby-on-rails

class Hotel
include Mongoid::Document
field :title, type: String
embeds_many :comments
end
class Comment
include Mongoid::Document
field :text, type: String
belongs_to :hotel
validates :text, presence: true
end
h = Hotel.create('hotel')
=> <#Hotel _id: 52d68dd47361731d8b000000, title: "hotel">
c = Comment.new(text: 'text')
=> <#Comment _id: 52d68f3d7361731d8b040000, text: "text", hotel_id: nil>
h.comments << c
=> [#<Comment _id: 52d68f3d7361731d8b040000, text: "text", hotel_id: nil>]
h.save
=> true
Hotel.last.comments
=> []
variant 2
h.comments << Comment.new(text: 'new', hotel_id: h.id)
=> [<#Comment _id: 52d68f3d7361731d8b040000, text: "text", hotel_id: nil>, <#Comment _id: 52d691e17361731d8b050000, text: "new", hotel_id: BSON::ObjectId('52d68dd47361731d8b000000')>]
h.save
=> true
Hotel.last.comments
=> []

I see two possible problems:
Hotel.last is not necessarily Hotel 52d68dd47361731d8b000000. You should look at h.comments or to be paranoid, h.reload and h.comments.
Your associations are confused.
From the fine manual:
Embedded 1-n
One to many relationships where the children are embedded in the
parent document are defined using Mongoid's embeds_many and
embedded_in macros.
Defining
The parent document of the relation should use the embeds_many macro
to indicate it has n number of embedded children, where the document
that is embedded uses embedded_in.
So your relation should be defined like this:
class Hotel
embeds_many :comments
end
class Comment
embedded_in :hotel
end
You're using belongs_to: hotel in Comment when you should say embedded_in :hotel.
The docs also say that:
Definitions are required on both sides to the relation in order for it to work properly.
and your relation is incorrectly configured on one side so it won't work properly.

Related

Association in mongoid

I have three models, Employee, Job, LeaveDay. like below:
class Employee
field :name
belongs_to :job
end
class Job
field :job_title
has_many :employees
has_many :leave_days
end
class LeaveDay
belongs_to :job
field :no_of_leave_days
end
I want to establish an relationship in which I want to track no_of_leave_days of employee related to their job. How is it possible. Thanks in advance.
i think u need to add 'belongs_to: employee' in LeaveDay and 'has_many: leave_days' in Employee. so, #emp.leave_days will get u all the leave for all the jobs of a particular employee. similarly, #emp.job.leave_days will return the leave_days for the current job of the cuttent employee.
I'm not really familiar with Mongoid. You may read here more about relations in mongoid.
It's very easy example. Hope it'll help.
This is models definition:
class Employee
include Mongoid::Document
field :name
belongs_to :job
end
class Job
include Mongoid::Document
field :job_title
has_many :employees
embeds_many :leave_days
end
class LeaveDay
include Mongoid::Document
embedded_in :job
field :no_of_leave_days
end
And example of usage:
pry(main)> j = Job.create(job_title: "Test Job")
=> #<Job _id: 510633e0784931179a000001, _type: nil, job_title: "Test Job">
pry(main)> LeaveDay.create(job: j, no_of_leave_days: 1)
=> #<LeaveDay _id: 5106348f784931179a000002, _type: nil, no_of_leave_days: 1>
pry(main)> LeaveDay.create(job: j, no_of_leave_days: 2)
=> #<LeaveDay _id: 51063687784931179a000003, _type: nil, no_of_leave_days: 2>
pry(main)> LeaveDay.create(job: j, no_of_leave_days: 3)
=> #<LeaveDay _id: 51063689784931179a000004, _type: nil, no_of_leave_days: 3>
pry(main)> Job.first.leave_days.map(&:no_of_leave_days)
=> [1, 2, 3]
pry(main)> Employee.create(name: 'Employee name', job: Job.first)
=> #<Employee _id: 510637cf784931179a000005, _type: nil, name: "Employee name", job_id: "510633e0784931179a000001">
pry(main)> Employee.first.job.leave_days.map(&:no_of_leave_days)
=> [1, 2, 3]

Sorting a Mongoid Object via its Associated Model's Attribute

I'm having some problems trying to understand how Mongoid does its sorting. I have 2 models, Gig and Venue, both of which are associated by a belong_to has_many relationship.
I'm trying to sort objects from Gig by the attribute 'name' of the Venue Object to no avail.
I'm hoping someone out there would be able to point me to the right direction.
Below are a truncated model description.
My Query is also below:
# Gig Model
class Gig
include Mongoid::Document
include Mongoid::Paperclip
include SearchMagic
belongs_to :owner, :class_name => "User", :inverse_of => :owns
belongs_to :venue
has_and_belongs_to_many :attenders, :class_name => "User", :inverse_of => :attending
has_and_belongs_to_many :artistes
<snip>
end
# Venue Model
class Venue
include Mongoid::Document
include Mongoid::Paperclip
include SearchMagic
has_many :gigs
field :foursquare_id, type: String
embeds_one :address
embeds_many :user_ratings
field :facebook, type: String
field :twitter, type: String
field :website, type: String
field :name, type: String
field :postal, type: String
field :tel, type: String
field :venue_type, type: String
field :description, type: String
field :rating, type: Float, default: 0.0
<snip>
end
# Console
>> Gig.desc('venue.name').map{|f| f.venue.name}
=> ["*SCAPE", "Velvet Underground", "Blujaz Lounge", "Velvet Underground", "Home Club", "Wh
ite House, Emily Hill", "Zouk", "Zouk", "The Pigeonhole", "Home Club", "Home Club", "Home C
lub"]
# sorting doesn't work
You can't join in mongo. If you need joins, use a relational database. A "feature" of non-relational databases is that you can't do joins.
You have basically two choices:
a before_save callback, which will inject the name of the venue into the gig as an additional field (see for instance https://github.com/rewritten/timebank/blob/master/lib/mongoid/denormalize.rb)
a map-reduce task, which after any modification of any venue or gig, will update the venue name into the gig as an additional field.
In the end, you need a field in the Gig collection to order it.

Mongoid Query for number of relationships

I have two models. I am trying to query a model for how many relationships it has to the other model. My models are as follows:
# app/models/client.rb
class Client
include Mongoid::Document
belongs_to :contact
...
end
# app/models/contact.rb
class Contact
include Mongoid::Document
has_many :clients
...
end
I need to be able to query for the following:
Contacts with NO clients
Contact.where("clients.length == 0")
Contacts with Clients
Contact.where("clients.length > 0")
Can anyone help me with how I would do about this?
Given the following model:
class Client
include Mongoid::Document
belongs_to :contact
field :name, type: String
end
class Contact
include Mongoid::Document
has_many :clients
field :name, type: String
end
And the following insertions:
Contact.create(:name => "Bill")
jill = Contact.create(:name => "Jill")
jill.clients.create(:name => "Steve")
The following code will do what you need:
p "Has Clients"
Contact.any_in(_id: Client.all.distinct("contact_id")).each do |c|
p c
end
p "No Clients"
Contact.not_in(_id: Client.all.distinct("contact_id")).each do |c|
p c
end
Outputs:
"Has Clients"
#<Contact _id: 4f5b04b1e98c373917000002, _type: nil, name: "Jill">
"No Clients"
#<Contact _id: 4f5b04b1e98c373917000001, _type: nil, name: "Bill">
Full gist:
https://gist.github.com/2010817

Mongoid nested has_one problem

I am trying to update a nested has_one model using mongoid but it will not persist the has_one association
im running Rails 3.07 & Mongoid 2.2
widget model
class Widget
include Mongoid::Document
embeds_many :permissions, :default => []
end
permission model
class Permission
include Mongoid::Document
field :admin, :type => Boolean, :default => false
has_one :user
embedded_in :widget
end
user model
class User
include Mongoid::Document
belongs_to :permission
end
Heres the results im getting from rails console;
#widget.permissions << Permission.new(:user => current_user)
=> [#<Permission _id: 4e5aced1c155df4b33000001, _type: nil, admin: false>]
#widget.save
=> true
#widget.permissions.first.user
=> #<User _id: 4e5ac71ec155df470f000001, _type: nil, email: "ada ..... >
Appears as if the user is saved, however it is not persisted to mongo.
The permission is being saved but has no user.
Any ideas?
Should you be using "embedded_in" rather than "belongs_to" in the User model?

Polymorphic associations in views

How do I display my polymorphic associations in my view? I have the following model.
class Blog < ActiveRecord::Base
belongs_to :users
has_one :category, :as => :categorizable
end
class Category < ActiveRecord::Base
belongs_to :categorizable, :polymorphic => true
end
class User < ActiveRecord::Base
has_many :blogs
has_many :categories, :as => :categorizable
end
However, my problem is, I get a nil when trying to display the category.
rails console
=> user = User.first
=> user.blogs
[]
=> user.categories
[]
=> category = user.categories
=> category = Category.new
=> category.name = "Education"
=> category.save!
true
=> user.categories
[#<Category id: 1, name: "Education", categorizable_id: 1, categorizable_type: "User", created_at: "...", updated_at: "...">]
=> user.blogs.create(:title => "...", :body => "...", :category => category)
[#<Blog id: 1, user_id: 1, title: "...", body: "...", created_at: "...", updated_at: "...">]
=> blogs = user.blogs.all
=> blogs.each do |blog|
puts blog.category
end
nil
=> blogs.first.category
[#<Category id: 1, name: "Education", categorizable_id: 1, categorizable_type: "User", created_at: "...", updated_at: "...">]
I don't get it, why blog.category is returning nil when I use the each block? How do I display the entries of my polymorphic model through my views?
Update:
The design is, as a user, I want to be able to create categories, and assign them to my blogs. Each blogs has one category on them, which I'd like to access via the Category model as a categorizable blog.
It should be really nice, if it's working as-is as the logic is trying to say, but currently it does not. Polymorphic models are supposed to be elegant, but right now, polymorphic sucks for me and unusable. I'm still waiting for someone to help me provide with a better solution.
There is a flaw in the design. Consider the following:
user = User.create
cat = user.categories.create(:name=>"Education")
Now there is one category:
> cat
# Category id: 1, name: "Education" categorizable_id:1 categorizable_type: "User"
When you add a blog and assign the same category to it:
user.blogs.create(:category=>cat)
It overwrites the polymorphic type and id:
# UPDATE "categories" SET "categorizable_type" = 'Blog', "categorizable_id" = 1,
"updated_at" = '2011-02-27 06:20:29.968336' WHERE ("categories"."id" = 1)
And now the category is no longer associated with the user:
user.reload
user.categories # => []
You're really trying to model a many to many relationship here. I'd suggest adding join tables for UserCategory and BlogCategory, and getting rid of the polymorphism, which isn't helping.
Pardon me for my earlier answer. zetetic's answer makes absolute sense. Try avoiding polymorphs.
Update: I've decided that polymorphic model was not the way to go, and I've solved it with good ol' STI which does the same thing.

Resources