Mongoid Query for number of relationships - ruby-on-rails

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

Related

How to save embeds_many relation in Mongoid?

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.

Mongoid: How to have distinct fields in User & Message n to m relation

I have got two models like following:
class User
include Mongoid::Document
has_many_and_belongs_to :messages
end
class Message
include Mongoid::Document
has_many_and_belongs_to :users
field :text
field :visited, type: Boolean, default: false
end
As far as i know when this template implements message fields are common among users. What i want to have here is visited field be distinct for every user to work with. Any idea how would that applicable? I have an alternative such as a middle model to save user & message id with desire field but that seems messy.
You need to build the has_and_belongs_to_many relation yourself. This way you can customize the class used to link a User and a Message:
class User
include Mongoid::Document
has_many :user_messages
end
class Message
include Mongoid::Document
has_many :user_messages
end
class UserMessage
include Mongoid::Document
belongs_to :user
belongs_to :message
field :visited, type: Boolean, default: false
scope :visited, -> { where(visited: true) }
scope :unvisited, -> { where(visited: false) }
end
Then you can do:
u = User.new
m = Message.new
u.user_messages.build(message: m, visited: true)
u.user_messages.build(message: m, visited: false)
u.user_messages.count
=> 2
u.user_messages.visited.count
=> 1
u.user_messages.unvisited.count
=> 1
More information about this kind of association from Rails Guide here.
Note that the UserMessage class name is quite annoying. You could instead use a different naming such as Conversation:
class User
has_many :messages
end
class Conversation
has_many :messages
end
class Message
belongs_to :user
belongs_to :conversation
field :visited, type: Boolean, default: false
end
Which is used nicely:
u = User.new
c = Conversation.new
u.messages.build(conversation: c)

Mongoid has_many association and factorygirl

I am using mongoid for the first time and I am having a problem creating a has_many association in a factory for my specs.
The scenario is this:
I have the a group class:
class Group
include Mongoid::Document
field :name, :type => String
end
And I have an exercise class. An exercise can belong to many groups. The exercise class is currently defined like this:
class Exercise
include Mongoid::Document
field :name, :type => String
field :description, :type => String
has_many :groups
validates_presence_of :name, :description
end
I want to use factorygirl to create instances for specs. I am struggling with how to do this.
Currently my exercise factory looks like this;
FactoryGirl.define do
factory :exercise do
name "Preacher curls"
description "Do something"
after(:build) do |exercise|
exercise.groups << FactoryGirl.build(:group)
end
end
end
This causes the following error:
NoMethodError:
undefined method `=' for #<Group _id: 4fbc6f5a26a3181742000004, _type: nil, name: "Arms">
How can I create the exercise factory correctly to add the group_ids?
Try to add
belongs_to :exercise
into your Group class
It should look like this:
class Group
include Mongoid::Document
field :name, :type => String
belongs_to :exercise
end

Mongoid: persistence in 1 to many relation within embedded document

I have a problem with relationships persisting in an embedded document. Here is the example
require 'mongoid'
require 'mongo'
class User
include Mongoid::Document
field :name
key :name
embeds_one :garage
end
class Garage
include Mongoid::Document
field :name
has_many :tools, autosave: true
has_many :cars, autosave: true
end
class Tool
include Mongoid::Document
belongs_to :garage, inverse_of: :tools
end
class Car
include Mongoid::Document
field :name
belongs_to :garage, inverse_of: :cars
end
When I run the following:
Mongoid.configure do |config|
config.master = Mongo::Connection.new.db("mydb")
end
connection = Mongo::Connection.new
connection.drop_database("mydb")
database = connection.db("mydb")
user = User.create!(name: "John")
user.build_garage
user.garage.cars << Car.create!(name: "Bessy")
user.save!
puts "user #{user}, #{user.name}"
user.garage.cars.each do |car|
puts "car is #{car}"
end
same_user = User.first(conditions: {name: user.name})
puts "same_user #{same_user}, #{same_user.name}"
same_user.garage.cars.each do |car|
puts "car found is #{car}"
end
the output is:
user #<User:0x00000003619d30>, John
car is #<Car:0x00000003573ca0>
same_user #<User:0x000000034ff760>, John
so the second time the user's car array is not outputted. How do i get the array of cars to persist?
Thanks
You need to do a user.garage.save to save the embedded garage after you populate it.

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?

Resources