I have two models that two relationships between them
class Tracking
include Mongoid::Document
belongs_to :origin_courier, :class_name => "Courier", foreign_key: "origin_courier_id"
belongs_to :destination_courier, :class_name => "Courier", foreign_key: "destination_courier_id"
end
class Courier
include Mongoid::Document
has_many :origins, class_name: 'Tracking', foreign_key: "origin_courier_id"
has_many :destinations, class_name: 'Tracking', foreign_key: "destination_courier_id"
end
When I assign a courier to the origin_courier of a newly created tracking, it run fine.
1.9.3p194 :015 > t = Tracking.new
=> #<Tracking _id: 4fbcc2772cfb397930000003, _type: nil, created_at: nil, updated_at: nil, origin_courier_id: nil, destination_courier_id: nil>
1.9.3p194 :016 > t.origin_courier = Courier.last
=> #<Courier _id: 4fbcbb072cfb397657000004, _type: nil, created_at: 2012-05-23 10:25:11 UTC, updated_at: 2012-05-23 10:25:11 UTC>
1.9.3p194 :017 > t
=> #<Tracking _id: 4fbcc2772cfb397930000003, _type: nil, created_at: nil, updated_at: nil, origin_courier_id: BSON::ObjectId('4fbcbb072cfb397657000004'), destination_courier_id: nil>
1.9.3p194 :018 > t.save
=> true
1.9.3p194 :019 > t
=> #<Tracking _id: 4fbcc2772cfb397930000003, _type: nil, created_at: 2012-05-23 10:57:14 UTC, updated_at: 2012-05-23 10:57:14 UTC, origin_courier_id: BSON::ObjectId('4fbcbb072cfb397657000004'), destination_courier_id: nil>
But when I assign a courier to the destination_courier of a newly created tracking, it duplicates the value to origin_courier
1.9.3p194 :020 > t2 = Tracking.new
=> #<Tracking _id: 4fbcc3002cfb397930000004, _type: nil, created_at: nil, updated_at: nil, origin_courier_id: nil, destination_courier_id: nil>
1.9.3p194 :021 > t2.destination_courier = Courier.last
=> #<Courier _id: 4fbcbb072cfb397657000004, _type: nil, created_at: 2012-05-23 10:25:11 UTC, updated_at: 2012-05-23 10:25:11 UTC>
1.9.3p194 :022 > t2.save
=> true
1.9.3p194 :023 > t2
=> #<Tracking _id: 4fbcc3002cfb397930000004, _type: nil, created_at: 2012-05-23 11:00:39 UTC, updated_at: 2012-05-23 11:00:39 UTC, origin_courier_id: BSON::ObjectId('4fbcbb072cfb397657000004'), destination_courier_id: BSON::ObjectId('4fbcbb072cfb397657000004')>
How can I resolve this?
Thanks.
(You mixed new Hash notaion (1.9.3) with older one in Tracking)
Did you try inverse_of instead of foreign_key ?
class Tracking
include Mongoid::Document
belongs_to :origin_courier, class_name: "Courier", inverse_of: :origins
belongs_to :destination_courier, class_name: "Courier", inverse_of: :destinations
end
and
class Courier
include Mongoid::Document
has_many :origins, class_name: 'Tracking', inverse_of: :origin_courier
has_many :destinations, class_name: 'Tracking', inverse_of: :destination_courier
end
https://github.com/mongoid/mongoid/issues/2038
It is bug of Mongoid.
Durran has fixed it.
Echo "very strange,"
The following modification also works, and I present it only because it might shed some light on the underlying problem.
class Courier
include Mongoid::Document
has_many :origin_trackings, class_name: "Tracking", foreign_key: :origin_tracking_id
has_many :destination_trackings, class_name: "Tracking", foreign_key: :destination_tracking_id
end
But I recommend NOT using foreign_key and DO second the recommendation to use inverse_of by Maxime Garcia.
For has_many/belongs_to, the relationship only has to be tracked on one side, even if the relationship is defined on both sides,
So like Active Record, Mongoid chooses for you, in this case to efficiently put the single link into the Tracking instance,
rather than growing an array of links in the Courier instance.
But a possible collision on foreign_key origin_courier_id for Tracking and Courier (or on foreign_key destination_courier_id for Tracking and Courier)
should not result in confusion between origin_* and destination_*.
I've looked at the metadata, which is only available for links that are set, and the metadata looks OK.
It's certainly a strange problem that would be interesting to investigate further as time and priorities permit.
Anyone else have some insight or knowledge of Mongoid internals?
Related
Can i get an id from associated relation?
for example i have relation between user and skill through user_skill
In User model, i write like this:
class User < ApplicationRecord
has_many ::skills, through: :user_skills
end
In UserSkill model
class UserSkill < ApplicationRecord
belongs_to :skill
belongs_to :user
end
I can call them like this
User.find(1).skills
but there is no user_skill id in there.
how to include id of user_skill? because everytime i call
User.find(1).skills
it will be like this
[#<Skill id: 3, name: "Google Analytics", description: "Google Analytics\r\n", created_at: "1970-01-04 11:20:00.000000000 +0000", updated_at: "1970-01-04 11:20:00.000000000 +0000", category: "software">, #<Skill id: 8, name: "Adobe After Effect", description: "Test", created_at: "2022-08-21 02:58:25.561498000 +0000", updated_at: "2022-08-21 02:58:25.561498000 +0000", category: "software">, #<Skill id: 1, name: "Microsoft Office", description: "Word, Excel, PowerPoint", created_at: "1970-01-02 03:46:40.000000000 +0000", updated_at: "1970-01-02 03:46:40.000000000 +0000", category: "software">]
You can do:
user_skills = User.find(1).user_skills.include(:skill)
user_skills.each do |user_skill|
user_skill.id #=> the user_skill id
user_skill.skill #=> the actual skill
end
I have a user model with an array of roles.
u=User.first
u.roles = ['admin']
v=User.last
u.roles = ['admin', 'member']
How do i query to fetch all users with admin role?
Edit:
roles is a serialized array for the user model
User Model:
serialize :roles
Migration:
add_column :users, :roles, :string, default: []
Depends on what "roles" means:
If it is an association as in
class User < ActiveRecord::Base
has_many :roles
end
Then you look up the role and fetch all users:
Role.find_by_name("admin").users
or roles is just a column and it is serialised, in which case you can instantiate all users (slow)
User.all.select { |u| u.roles.include? "admin" }
Or query the database directly which is more complex and depends on the adapter.
My opinion: I would avoid using serialised columns, when relations can do. They are cumbersome in every way: Forms, Searches, Selects...
Another way is using scopes:
Model user.rb:
class User
scope :admins, -> () { where("roles #> ?", '{admin}')}
scope :by_roles, -> (roles) { where("roles #> ?", "{#{roles.join(', ')}}") }
end
Scoped calls, with results
Admins:
3.0.0 :001 > User.admins
User Load (0.4ms) SELECT "users".* FROM "users" WHERE (roles #> '{admin}') /* loading for inspect */ LIMIT $1 [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<User id: 1, account_uuid: "0812df2d-58ed-4231-aca5-ec7708a156bb", email: "admin#example.com", password_digest: [FILTERED], first_name: "Admin", last_name: "Admin" remember_token: nil, remember_token_expires_at: nil, roles: ["user", "admin"], created_at: "2021-12-02 11:51:50.684788000 +0000", updated_at: "2021-12-02 11:51:50.684788000 +0000">]>
Users by_roles within single role:
3.0.0 :002 > User.by_roles(['user'])
User Load (0.5ms) SELECT "users".* FROM "users" WHERE (roles #> '{user}') /* loading for inspect */ LIMIT $1 [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<User id: 1, account_uuid: "0812df2d-58ed-4231-aca5-ec7708a156bb", email: "admin#example.com", password_digest: [FILTERED], first_name: "Admin", last_name: "Admin", remember_token: nil, remember_token_expires_at: nil, roles: ["user", "admin"], created_at: "2021-12-02 11:51:50.684788000 +0000", updated_at: "2021-12-02 11:51:50.684788000 +0000">, #<User id: 2, account_uuid: "7a09830d-bfde-45eb-abeb-4b305a7bb374", email: "user_1#example.com", password_digest: [FILTERED], first_name: "User", last_name: "Adviseman", remember_token: nil, remember_token_expires_at: nil, roles: ["user"], created_at: "2021-12-02 17:40:12.172547000 +0000", updated_at: "2021-12-02 17:40:12.172547000 +0000">, #<User id: 3, account_uuid: "a0044954-6ad0-4d8f-9681-bd5dfcfa72b7", email: "user_2#example.com", password_digest: [FILTERED], first_name: "User", last_name: "Adviseman", remember_token: nil, remember_token_expires_at: nil, roles: ["user"], created_at: "2021-12-02 17:40:12.583594000 +0000", updated_at: "2021-12-02 17:40:12.583594000 +0000">, #<User id: 4, account_uuid: "7d26e5d5-beaf-48bd-ad98-f53044a7182b", email: "user_3#example.com", password_digest: [FILTERED], first_name: "User", last_name: "Adviseman", remember_token: nil, remember_token_expires_at: nil, roles: ["user"], created_at: "2021-12-02 17:40:12.847412000 +0000", updated_at: "2021-12-02 17:40:12.847412000 +0000">]>
Users by_roles within multiple roles:
3.0.0 :003 > User.by_roles(['user', 'admin'])
User Load (0.6ms) SELECT "users".* FROM "users" WHERE (roles #> '{user, admin}') /* loading for inspect */ LIMIT $1 [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<User id: 1, account_uuid: "0812df2d-58ed-4231-aca5-ec7708a156bb", email: "admin#example.com", password_digest: [FILTERED], first_name: "Admin", last_name: "Bezugliy", remember_token: nil, remember_token_expires_at: nil, roles: ["user", "admin"], created_at: "2021-12-02 11:51:50.684788000 +0000", updated_at: "2021-12-02 11:51:50.684788000 +0000">]>
So I have an Array filled of what exactly?
myArray = [ #<Follower id: 1, username: "Prep Bootstrap", imageurl: "http://pbs.twimg.com//profile_images/2825468445/2a4...", user_id: "thefonso", follower_id: "2397558816", created_at: "2014-05-21 15:29:03", updated_at: "2014-05-21 15:29:03">, #<Follower id: 2, username: "JAVA Developer", imageurl: "http://pbs.twimg.com//profile_images/2825468445/2a4...", user_id: "thefonso", follower_id: "2352382640", created_at: "2014-05-21 15:29:05", updated_at: "2014-05-21 15:29:05">,
Follower id: 3, username: "JAVA Developer two", imageurl: "http://pbs.twimg.com//profile_images/2825468445/2a4...", user_id: "thefonso", follower_id: "2352382641", created_at: "2014-05-21 17:29:05", updated_at: "2014-05-21 17:29:05"> ]
Now I have many more of these inside this array with consecutive ids, etc..I'm confused by the #< >, is this an array of hashes? What am I looking at exactly? What is this an array of?
It looks like ActiveRecord's Followers class instances. Plus, it looks like you named your model improperly (with Rails practices, it should be named Follower).
Get the class of one of the objects and you'll know it
puts myArray[0].class
My check_processors method should go through an array of processors (provided by the processor_list argument) and output a new array, containing the same processors.
The processor_list argument is either nil or an array of processors, e.g.:
[
<Processor _id: 5158713883c336812100007f, _type: nil, created_at: 2013-03-31 17:24:08 UTC, updated_at: 2013-03-31 17:24:08 UTC, name: "my_mapping_processor", value: {"something"=>{"something_else"}}, type: "mapping", order: 0>,
<Processor _id: 5158713883c3368121000080, _type: nil, created_at: 2013-03-31 17:24:08 UTC, updated_at: 2013-03-31 17:24:08 UTC, name: "my_presentation_processor", value: {"something"=>{"something_else"}}, type: "presentation", order: 1>,
<Processor _id: 5348143r83c93641210d0080, _ty...
]
The check_processors method should verify that 2 specific processors (recognized by the type attribute of a processor) exists. If no processor with type == "mapping" is found, create_mapping_processor should it. Or if no processor with type == "presentation" is found, create_presentation_processor should create it.
The code below works, but the job it is doing is so simple that it must be achievable with less code.
def check_processors(processor_list = nil)
processors = []
if processor_list == nil
mapping_processor = create_mapping_processor
presentation_processor = create_presentation_processor
processors.push(mapping_processor)
processors.push(presentation_processor)
else
mapping_processor_exists = false
presentation_processor_exists = false
processor_list.each do |processor|
processors.push(processor)
mapping_processor_exists = true if processor.type == "mapping"
presentation_processor_exists = true if processor.type == "presentation"
end
if mapping_processor_exists == false
mapping_processor = create_mapping_processor
processors.push(mapping_processor)
end
if presentation_processor_exists == false
presentation_processor = create_presentation_processor
processors.push(presentation_processor)
end
end
processors #=> [<Processor _id: 5158713883c336812100007f, _type: nil, created_at: 2013-03-31 17:24:08 UTC, updated_at: 2013-03-31 17:24:08 UTC, name: "my_mapping_processor", value: {"something"=>{"something_else"}}, type: "mapping", order: 0>, <Processor _id: 5158713883c3368121000080, _type: nil, created_at: 2013-03-31 17:24:08 UTC, updated_at: 2013-03-31 17:24:08 UTC, name: "my_presentation_processor", value: {"something"=>{"something_else"}}, type: "presentation", order: 1>, <Processor _id: 5348143r83c93641210d0080, _ty...]
end
def create_mapping_processor
mapping_processor = Processor.new(:name => "my_mapping_processor", :value => {"something"=>{"something_else"}}, :type => "mapping", :order => 0)
mapping_processor
end
def create_presentation_processor
presentation_processor = Processor.new(:name => "my_presentation_processor", :value => {"something"=>{"something_else"}}, :type => "presentation", :order => 1)
presentation_processor
end
I've created Trainer and Client models that are associated via a has_many/belongs_to relationship: Trainer has_many Clients, and Client belongs_to Trainer. However, when trying to add an existing client to an existing trainer using Trainer.Clients << Client, it does not save to the database.
Client model:
class Client < ActiveRecord::Base
attr_accessible :email, :name, :password, :password_confirmation, :trainer_id
has_secure_password
belongs_to :trainer
validates :name, presence: true, length: { maximum: 40 }
validates :password, presence: true, length: { minimum: 6 }
validates :password_confirmation, presence: true, length: { minimum: 6 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
before_save { |client| client.email = email.downcase }
end
Trainer model:
class Trainer < ActiveRecord::Base
attr_accessible :email, :name, :password, :password_confirmation
has_secure_password
has_many :clients
validates :name, presence: true
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 6 }
validates :password_confirmation, presence: true, length: { minimum: 6 }
before_save { |trainer| trainer.email = email.downcase }
end
The Client table in the database has a foreign_id trainer_id:
create_table "clients", :force => true do |t|
t.string "name"
t.string "email"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "password_digest"
t.integer "trainer_id"
end
In the console, when I try to add a client record to a trainer, it fails to save to the database:
1.9.3-p194 :001 > trainer = Trainer.find(2)
Trainer Load (30.1ms) SELECT "trainers".* FROM "trainers" WHERE "trainers"."id" = ? LIMIT 1 [["id", 2]]
=> #<Trainer id: 2, name: "Noah Macejkovic", email: "trainer-1#mp-trainer.com", created_at: "2012-06-06 20:40:45", updated_at: "2012-06-06 20:40:45", password_digest: "$2a$10$bDL0ealWYdsxpN2jOyToCO9T/WKsI4I7WXdhRXQBvcP1...">
1.9.3-p194 :002 > client = Client.find(2)
Client Load (0.2ms) SELECT "clients".* FROM "clients" WHERE "clients"."id" = ? LIMIT 1 [["id", 2]]
=> #<Client id: 2, name: "Katherine Skiles V", email: "client-1#example.com", created_at: "2012-06-06 20:40:54", updated_at: "2012-06-06 20:40:54", password_digest: "$2a$10$47CUSOS3k5j1c.DjH2PP7uqHD6kM94rZHMco37X34li/...", trainer_id: nil>
1.9.3-p194 :003 > trainer.clients << client
(0.1ms) begin transaction
Client Exists (0.4ms) SELECT 1 FROM "clients" WHERE (LOWER("clients"."email") = LOWER('client-1#example.com') AND "clients"."id" != 2) LIMIT 1
(0.1ms) commit transaction
=> false
If I query the client immediately after the transaction, it shows the trainer_id
1.9.3-p194 :004 > client
=> #<Client id: 2, name: "Katherine Skiles V", email: "client-1#example.com", created_at: "2012-06-06 20:40:54", updated_at: "2012-06-06 20:40:54", password_digest: "$2a$10$47CUSOS3k5j1c.DjH2PP7uqHD6kM94rZHMco37X34li/...", trainer_id: 2>
But after reloading from the database, the trainer_id is nil (as expected because of the "false" return from trainer.clients << client.
1.9.3-p194 :006 > client.reload
Client Load (0.2ms) SELECT "clients".* FROM "clients" WHERE "clients"."id" = ? LIMIT 1 [["id", 2]]
=> #<Client id: 2, name: "Katherine Skiles V", email: "client-1#example.com", created_at: "2012-06-06 20:40:54", updated_at: "2012-06-06 20:40:54", password_digest: "$2a$10$47CUSOS3k5j1c.DjH2PP7uqHD6kM94rZHMco37X34li/...", trainer_id: nil>
So the crux of the question is "Why does the commit to the database fail?"