I have model structure like this:
class Forum < ActiveRecord::Base
has_many :topics
class Topic < ActiveRecord::Base
has_many :posts
belongs_to :forum
class Post < ActiveRecord::Base
belongs_to :topic
has_many :post_links
has_many :links, ->{ uniq }, through: :post_links, dependent: :destroy
class PostLink < ActiveRecord::Base
belongs_to :post
belongs_to :link
class Link < ActiveRecord::Base
has_many :post_links
has_many :posts, ->{ uniq }, through: :post_links, dependent: :destroy
Now I want to get all Links from Forum with id = 1, sorted by how often they show up in posts in this forum.
Link.joins(posts: [topic: :forum]).where("forums.id = ?",1).group("links.id").order("count_all DESC").count
It gives me hash like {140 => 10, 12 => 9, 137 => 8}
I'd like to have Link relation instead of hash with ids, but I'm not sure how to change this query.
Using select in the query should return an ActiveRecord::Relation. Try this:
Link.select("links.*, COUNT(links.id) as link_count").joins(posts: [topic: :forum]).where("forums.id = ?",1).group("links.id").order("link_count DESC").count
Since I don't have your model structure and relations I can't test this, but I did it with a similar query. Using selectyou will get a Relation.
links.* will return all columns/attributes of the Link model. In case you only need specific attributes (like the url of the link) just use link.url instead.
I managed to solve it with this query:
Link.joins(posts: [topic: :forum]).where("forums.id = ?",1)
.group("links.id").order("count_links_id DESC")
.select("links.*, COUNT(links.id) AS count_links_id")
Since you are fetching the count form the maximum grouped ID's you cannot directly get the active record relation but,
You can just fire a where condition in the Link model from the returned result to get the Active record association,
Link.where(id: (Link.joins(posts: [topic: :forum]).where("forums.id = ?",1).group("links.id").order("count_all DESC").count).keys)
Returns,
#<ActiveRecord::Relation [#<Link...]
Related
I have models:
User
class User < ActiveRecord::Base
has_many :users_conversations, dependent: :destroy
has_many :conversations, through: :users_conversations, dependent: :destroy
end
UsersConversation
class UsersConversation < ActiveRecord::Base
belongs_to :conversation
belongs_to :user
end
Conversation
class Conversation < ActiveRecord::Base
has_many :users_conversations, dependent: :destroy
has_many :users, through: :users_conversations
end
I need to create query to get the joint conversation between two known users by their ID's. I have this one
Conversation.left_joins(:users).where(users: { id: [current_user.id, params[:user_id]]}).first
but it finds conversations where is any of [current_user.id, params[:user_id]], so I always get the same conversation, where is current_user, but it is wrong. As I see it is because id: [current_user.id, params[:user_id]] works like OR, but I need AND. Thank you.
The answer is not correct as mentioned in the comments. Please do not refer it.
Try using
Conversation.left_joins(:users).find_by("users.id IN(?)", [current_user.id, params[:user_id]]})
Haven't actually tried but this should work. Please let me know if you face an error.
Also, use find_by instead or Model.where(condition).first. Using the latter is a bad habit and convention in rails.
Explanation: When you have to find the records where a field matches an array, i.e., you need the value of this filed to be specific from an array, you should use IN operator of SQL. Like
SELECT * FROM Customers WHERE Country IN ('Germany', 'France', 'UK');
I'm trying to access all comments from a given user with user.comments. The query is to go through two different models, which likely both return results. My relations are set up as follow:
class User < ActiveRecord::Base
has_many :organisers
has_many :participants
has_many :comments, through: :participants / :organisers (see explenation below)
end
class Organiser < ActiveRecord::Base
belongs_to :user
end
class Participant < ActiveRecord::Base
belongs_to :user
end
class Comment < ActiveRecord::Base
belongs_to :organiser
belongs_to :participant
end
A comment is validated to belong to either a participant, or an organiser.
I'm not sure how to go about this. I've tried
has_many :comments, through: :participants
has_many :comments, through: :organisers
and
has_many :comments, through: [:organisers, :participants]
But that last one isn't rails. Is there a proper way to do this? Thanks!
has_many :comments, ->(user) {
unscope(where: :user_id).
left_joins(:organizer, :participant).
where('organizers.user_id = ? OR participants.user_id = ?', user.id, user.id)
}
The unscope is to remove the comments.user_id = ? clause (which is added by default when you define a has_many relation).
The left_joins is called on Comment, so you need to pass in the relation names as defined on Comment, hence the singulars in this example.
I found a solution after many tries. You can use a scope with param in your last has_many sentence in the User model:
has_many :comments, -> (user) {where organiser: user.organisers}, through: :participants
The "user" param represet the User object whom is calling the comments method.
For anyone coming across this using polymorphic associations, the following worked for me inspired by magni- and Carlos Jimenez' answers:
has_many :comments, -> (user) {
unscope(where: :user_id).
where(commentable: [user.organizers, user.participants])
}
Since we couldn't use has_many, through here because comments come from both of organisers and participants. I just think there are 2 solutions here:
Solution #1 Define comments method:
class User < ActiveRecord::Base
def comments
Comment.joins([{organiser: :user}, {participant: :user}])
.where(users: {id: self.id})
end
end
So then your query to find comments is:
User.first.comments
Solution #2 Use scope in Comment
class Comment < ActiveRecord::Base
scope :from_user, -> (user) {
joins([{organiser: :user}, {participant: :user}]).where(users: {id: user.id})
}
end
So your query will be like:
user = User.first
comments = Comment.from_user(user)
Since we couldn't use has_many, through here because comments come from both of organizers and participants. I just think there are 2 solutions here:
Basically you can still change the foreign key to accept the self.id automatically with Rails here
User.first.comments
class User
has_many :comments, -> { joins([{ organiser: :user }, { participant: :user }]) }, through: :participants, foreign_key: "users.id"
end
I believe your associations would be confused, as user.comments wouldn't know whether it's going through Participant or Organiser, so the best option would be to declare two different joins (with different names):
http://guides.rubyonrails.org/association_basics.html#self-joins
My Models are set up as follows:
class Record < ActiveRecord::Base
belongs_to :activity
has_many :codes, -> { order("codes.name") }, through: :activity
class Activity < ActiveRecord::Base
has_many :activity_code_links
has_many :codes, through: :activity_code_links
has_many :records
class ActivityCodeLink < ActiveRecord::Base
belongs_to :activity
belongs_to :code
class Code < ActiveRecord::Base
has_many :activity_code_links
has_many :activities, through: :activity_code_links
has_many :records, through: :activities
In a controller, i have an ActiveRecord Relation of records, #records.
I would like my resulting #records to only contain Records that have a specific Code.
Currently, i am using the below solution; however it is not efficient and also returns an Array, not an ActiveRecord Relation:
#records = #records.reject { |record| (record.codes & [code]).empty? }
Any help would be appreciated
Cheers
#records.joins(:codes).where(codes: {id: code.id})
Please note that if you don't need to use codes entities later you should use joins instead of includes since it won't instantiate ActiveRecord objects which adds overhead
#records = #records.includes(:codes).where(codes: {id: code.id})
Where code is a Code object which is the one you want to be filtered.
I am looking for a way to query a model based on children in a has_many through association.
I have 3 models:
class Conversation < ActiveRecord::Base
has_many :conversations_participants
has_many :participants, through: :conversations_participants
end
class ConversationsParticipant < ActiveRecord::Base
belongs_to :conversation
belongs_to :participant, class_name: 'User'
end
class User < ActiveRecord::Base
has_many :conversations_participants
has_many :conversations, through: :conversations_participants
end
I need to find conversations where participants matches an array of ids.
This is what i have at the moment (not working):
Conversation.includes(:participants).where(participants: params[:participants])
Sounds like you just want the conversations, if so you can joins.
Conversation.joins(:participants).where(:users => { :id => params[:participants] } )
Otherwise, if you want to eager load the participants, use includes
Conversation.includes(:participants).where(:users => { :id => params[:participants] } )
You can pass an array like this:
Conversation.includes(:participants).where(:id => params[:participants])
Assuming that params[:participants] is an array.
Conversation.includes(:participants).where( 'conversation_participants.participant_id' => params[:participants])
assuming that participant_id is the foreign key of participants in the conversation_participants table
I'm having trouble with a simple has_many relationship. I'm trying to return unique objects, but because I'm putting restraints on the object in the middle of the relationship, I'm getting an undefined method on my ActiveRecord_AssociationRelation.
I have:
class Customer < ActiveRecord::Base
has_many :session_enrollments
has_many :session_details, through: :session_enrollments
class SessionDetail < ActiveRecord::Base
has_many :customers, through: :session_enrollments
belongs_to :trainer
class Trainer < ActiveRecord::Base
has_many :session_details
has_many :session_enrollments, through: :session_details
has_many :customers, through: :session_enrollments
class SessionEnrollment < ActiveRecord::Base
belongs_to :customer, :foreign_key => "customer_id"
belongs_to :session_detail, :foreign_key => "session_detail_id"
I'm trying to return all trainers that have a current session with a customer via:
Customer.rb
#trainers = self.session_details.where(state: ["CONFIRMED"]).trainers
But the where statement loads an active record association object. How can I include this condition and still return the unique trainers?
Try this:
#trainers = Trainer.joins(:session_details, :customers).where(session_details: {state: "CONFIRMED"}).where(customers: {id: self.id}).distinct
I'd try this:
#trainers = Trainer.joins(session_details: [:session_enrollments]).where(session_details: {state: 'CONFIRMED'}).where(session_enrollments: {customer_id: self.id}).distinct
I'm assuming that state is a column in Customer, You need to join the records, try this instead
#trainers = self.includes(:session_details => :trainer).where(session_details: {state: ["CONFIRMED"]})
This query will return customer along with their session details with associated trainers