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
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.
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)
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
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.
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?