Query to find all messages a user is participating in - sql

I'm creating a messaging system. A user can start a conversation by messaging multiple recipients who in turn can reply back.
I want to find all conversations a user is participating in. Ie. conversations where they have either authored a message or were a recipient of a message in the conversation.
Currently my models looks like this
# Message
belongs_to :conversation
belongs_to:user #author
has_many :receipts
has_many :recipients, :through => :receipts
# Conversation
has_many :messages
# User
has_many :receipts
has_many :incoming_messages, through: :receipts, :class => 'Message'
# Receipt
belongs_to :message
belongs_to :user
I want to create a scope on Conversation to achive something like this
Conversation.involving(user)
Not sure how to write the scope / sql for this. It feels like I need the equivalent of an OR statement in there.
ie. in PSEUDO CODE
conversations where (messages.recipient_ids include user.id OR messages.user_id == user.id)
I'm assuming ive modeled the system correctly. If not any better schema suggestions would also be greatly appreciated.
Can anyone help with either.

Something like this should work:
Conversation.includes(messages: :receipts).where(["messages.user_id = :user_id OR receipts.user_id = :user_id", user_id: user.id])
This is doing the where clauses on the user_id foreign keys so that you don't have to figure out what table aliases ActiveRecord would assign to the users table (since it would be joined twice).

Related

Extra Role column in join table in Rails

I have users and companies in a many to many relationship by a join table which has a column for user Role. I'm not sure if this is the best way to have the model set up.
Now each user can have different roles depending on the company, what is the best way to design and access user's role using ActiveRecord associations?
I would like to return via JSON the user's role based on their current company and default to something if their company is nil OR their role has not been set (nil).
Update:
What I've got now after reading Many-to-many relationship with the same model in rails? which is a bit different (many to many on itself).
CompaniesUser
belongs_to :company
belongs_to :user
Company
has_many(:companies_users, :dependent => :destroy)
has_many :users, :through => :companies_users
User
has_one :company
has_many(:companies_users, :dependent => :destroy)
has_many :companies, :through => :companies_users
Appreciate any advice as I'm just starting to learn this!
What you have above is correct, in terms of the ActiveRecord relationships. If you'd like to read more on the subject I believe this is the best source: http://guides.rubyonrails.org/association_basics.html
One problem I see there is that CompaniesUsers should be in singular form: CompanyUser, and then in all cases where you use :companies_users use: :company_users
I am assuming here that the current company of the User is the last one assigned.
Now in order to serialize in JSON format you should add the following in your User ActiveRecord:
def serializable_hash(options = nil)
options ||= {}
h = super(options)
if(defined?self.company_users.last and defined?(self.company_users.last).role)
h[:role] = (self.company_users.last).role
else
h[:role] = 'default_value'
end
end

Rails Search Self Referential Relationship

I am trying to allow users to search through their own friends by email address. I'd like to do something like:
current_user.search('test#fake.com')
and have it return an array of current users friends that have that email address.
So I have a very basic friendship relationship set up on my user model
user.rb
has_many :friendships
has_many :friends, through: :friendships, source: :friend
has_many :inverse_friendships, class_name: 'Friendship', foreign_key: 'friend_id'
has_many :inverse_friends, through: :inverse_friendships, source: :user
friendship.rb
belongs_to :friend, class_name: 'User', foreign_key: 'friend_id'
belongs_to :user
I want to set up a method on my user model that can search through their friends by email address. It's not working so well
def search(query)
conditions = ['friends.user_id = ? AND email LIKE ? ', self.id, "%#{query}%"]
User.includes(:friends).where(conditions)
end
I guess I'm just not sure how to format my active record query / SQL here, since I am trying to search on the relations of a self referential model. Any one have any ideas?
Thanks!
Digital Cake is going in the right direction, but not exactly correct. A scope is a method of User, not user. What you need is:
def followers_by_email(email)
friends.where("email like ?", "%#{email}%")
end
This returns an ActiveRecord::Relation to which you can chain other conditions, order, paginate, etc as in
user.followers_by_email("me#example.com").order(:first_name).limit(10)
Good time to use active record scopes. http://guides.rubyonrails.org/active_record_querying.html#scopes
Heres a simple example
user.rb
scope :followers, friends.where(:published => true).order("created_at DESC").limit(150)
in your controller
#followers = User.followers
I seem to have some success with:
conditions = ['contacts.user_id = ? AND users.email LIKE ? ', self.id, "%#{query}%"]
User.includes(:inverse_friends).where(conditions)
Though it's strange that it works, I'm not entirely sure why it does.

Rails 3 - associations

I print in my view a number that tell me, how many people read my article. It looks something like a:
<%=article.hits.count%>
As is possible to see, I created a simple association.
Now I am trying to get the information, if the user who is log in on my page, so if he is already had read this article. In my table that contains hits is column user_id.
But I can't still find the way, how to get...
I tried something like:
<% if session[:login_user_id].hits.user_id == session[:login_user_id]%>
Have you read it already.
<% end %>
But the example above doesn't work me... Could anyone help me please, how to do?
EDIT: The models:
class Article < ActiveRecord::Base
has_many :hits
end
class Hits < ActiveRecord::Base
belongs_to :article, :class_name => "DataHit", :foreign_key => "article_id"
has_many :users
end
class User < ActiveRecord::Base
belongs_to :hit
end
Thanks in advance
Let's first talk about the model you like to receive. For me, it sounds like:
Every article can be visited / read by many users.
Every user can read / visit many articles.
This is a classical n:m-association which is normally implemented by a has-many-through association.
If this is the intention, it should be implemented like:
class Article < ActiveRecord::Base
has_many :hits
has_many :users, :through => :hits
end
class Hits < ActiveRecord::Base
belongs_to :article, :class_name => "DataHit", :foreign_key => "article_id"
belongs_to :user
end
class User < ActiveRecord::Base
has_many :hits
has_many :articles, :through => :hits
end
Of course, you have to add migrations that ensure that the final DB model is like that:
Hit has article_id and user_id to ensure that users may find the articles they have read
If you have that model implemented, it should be more easy. Then you have operations available like: #article.users.contains(User.find(user_id)). Have a look at the tutorial at Ruby on Rails Guides which explain what the has-many-through relation is and which advantages they have.
It would be helpful if you try the things first in the console of Rails. To do that, start with:
Start the rails console in the root directory of your application: rails c
Enter there e.g.: art = Article.find(1) to get the article with the id.
Try which methods are available: art.methods.sort to see all methods that could be used. If there is no method users, you have did something wrong with the assocication.
Try the call: us = art.users and look at the result. It should be a rails specific object, an object that behaves like a collection and understands how to add and remove users to that collection (with the whole life cycle of rails). The error your currently have could mean different things:
Your database model does not match your associations defined in Rails (I suspect that).
Some minor tweak (misspelling somewhere) which hinders Rails.
I hope this gives you some clues what to do next, I don't think that we can fix the problem here once and for all times.

Ruby on Rails: has_many referential --which model objects does it own?

I am new to Rails and finished Michael Hartl's "Ruby on Rails 3 Tutorial". Although the book teaches me a lot, I find this puzzle I don't understand.
To preview the puzzle, that is, I don't understand, inside User model,
has_many :following, :through=>:relationship, :source=>:followed
how this piece of code link "user.following" to an array of User instances.
And below is the whole puzzle.
First of all, I have the Relationship model, which records followed_id and follower_id infos. Inside Relationship model, the association is simple as
class Relationship < ActiveRecord::Base
attr_accessible :followed_id
belongs_to :follower, :class_name => "User"
belongs_to :followed, :class_name => "User"
end
Then, inside the User model, a user will assume the role of follower, and collect all its following rows in relationships table through relationships association.
class User < ActiveRecord::Base
.
.
.
has_many :relationships, :foreign_key => "follower_id", :dependent => :destroy
.
Until now, I got it.
But confusion came at the next line, where through user.following it can assemble all that user's following(User instances). Like so,
has_many :following, :through=>:relationships, :source=>:followed
I understand that :source=>:followed will overwrite the default, and let find all followed_ids associated with that user.
But, how can Rails recognize followed_id to link to User object? The label name doesn't match users, nor is there :class_name specified. I just don't get how Rails do this underlying work, or I missed out some hints.
Thank you! :)
But, how can Rails recognize followed_id to link to User object? The
label name doesn't match users, nor is there :class_name specified. I
just don't get how Rails do this underlying work, or I missed out some
hints.
Rails recognize that is an user object because it is set in Relationship's belongs_to. What Rails does here is to follow the relationship class through the foreign key "follower_id" and returning every User that has a relationship with the current user as followed. Of course Rails do that in a single SQL statement like this:
SELECT `users`.* FROM `users` INNER JOIN `relationships` ON `relationships`.followed_id = `users`.id WHERE ((`relationships`.follower_id = <the current user id> ))
has_many :following, :through=>:relationships, :source=>:followed
This explains to Rails that following is the inverse relationship of following and that users has many following and followed through his relationships.
The way Rails knows that followed_id is linked to User is that it is defined in your Relationship model.
Hope you've understood ! Good luck :)

Post belongs to Topic and User. How do I create a Post with :topic_id and :user_id without exposing either with attr_accessible?

I'm making a conventional forum to learn/practice Rails. As you're familiar with, Users can create Topics and Posts. Topics and Posts belong to the User that created them, and Posts belong to the Topic they were posted in. Note: Post is a nested resource of Topic.
User Model
has_many :topics
has_many :posts
Topic Model
has_many :posts
belongs_to :user
Post Model
belongs_to :user
belongs_to :topic
So, when a User creates a new Post, the Post needs a user_id and a topic_id.
I know about scoping associations with:
#user.posts.create(:title => "My Topic.")
#topic.posts.create(:content => "My Post.")
But those examples only set user_id and topic_id respectively, not both.
My Question
How can I do something like this:
#topic.posts.create(:content => "This is Dan's post", :user_id => #dan.id)
Without having to expose user_id in the Post model via attr_accessible :user_id?
In other words, I don't want to have to explicitly define :user_id.
I tried things like:
dans_post = #user.posts.new(:content => "the content of my post")
#topic.posts.create(dans_post)
to no avail.
Use build for building associations, instead of new, as it will define the foreign key correctly. To solve your problem, use merge to merge in the user to the parameters for the post:
#topic.posts.build(params[:post].merge(:user => current_user))