rails associations, retrieve conversation between 2 users - sql

I have a rails app using postgres database, and I'm trying to add a feature where 2 users can directly chat with each other. I a user model and conversation model, and they are related by has_and_belongs_to_many relationship. I also has a joint table for this relationship.
class CreateJoinTableUserConversation < ActiveRecord::Migration
def change
create_join_table :users, :conversations do |t|
end
end
end
My goal is when a user clicks on the link to chat with another user, if there is no conversation object related to these 2 users, then create one. Otherwise retrieve the existing conversation. How can I do this with either active record or a sql call? I don't want to have to iterate through all the conversations and perform a check on whether or not these 2 users are in conversation.users, because that will take forever when there are a lot of conversations. Is there a way to access the joint table to somehow retrieve the record of a conversation that has 2 particular users associated with it?
EDIT
here is the table from the schema.
create_table "conversations", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
here are the models:
class Conversation < ActiveRecord::Base
has_and_belongs_to_many :users
end
class User < ActiveRecord::Base
has_and_belongs_to_many :conversations
end

Instead of has_and_belongs_to_many relationship, you can use has_many: through relationship. Which will give you joint table model as in your case Conversation model.So that you can perform where query on your joint table using model.
But you will also need to add foreign keys of both the users in join table.

Related

SQL relations in Rails. Trying to create two different references for one table relationship. Very confusing situation

I have a DB schema where a shipment belongs_to two users even though the relation is between model to model.
The shipment can have a receiver_id and a sender_id. Both the receiver & sender are two different records in the same table (the user model).
I was wondering how can I establish a relationship in my rails backend for a shipment to belong_to a record where the user is the sender as well as another record in the same DB where the user is the receiver.
I'v been thinking for ages how to do it, and its very hard to come up with a solution. The only solution I have is to destroy the existing User model, and create two different models: a receiver & a sender. Or is there another solution for this? I've attached my DB schema in this thread.
Any help would much appreciated. Thanks in advance!
DB Schema
class Shipment < ApplicationRecord
belongs_to :sender, :class_name => "User"
belongs_to :recipient, :class_name => "User"
end
class User < ApplicationRecord
has_many :sent_shipments, :class_name => "Shipment", foreign_key: 'sender_id'
has_many :received_shipments, :class_name => "Shipment", foreign_key: 'recipient_id'
end

Rails includes with where condition

I have two models:
user.rb
class User < ApplicationRecord
has_many :orders, inverse_of: :user, dependent: :restrict_with_exception
end
order.rb
class Order < ApplicationRecord
belongs_to :user, inverse_of: :orders
end
I am using includes with where like this:
User.where(id: selected_salesmen.pluck(:id))
.includes(:orders)
.where("order.booked_at > ? AND order.booked_at < ?",
booked_at_gteq,
booked_at_lteq)
However, it's not giving me required users with orders. Any explanation of why this isn't working?
This may be some confusing between the methods includes and joins, which have related, but very different meanings.
includes will eager-load related records, which prevents multiple database calls later on. It's mostly used for performance tuning.
joins will include a join to the related table in your database query, which allows you to build conditions based on the related model.
Note that, to use joins, you need to refer to the table name, not the relation name. By default, ActiveRecord will connect to a table which is the pluralised name of the model.
So, change includes to joins and 'order' to 'orders':
User.where(id: selected_salesmen.pluck(:id))
.joins(:orders)
.where(
"orders.booked_at > ? AND orders.booked_at < ?",
booked_at_gteq,
booked_at_lteq
)
You may also want to check that selected_salesmen.pluck(:id) returns some ids, too.

How to sub-query on the "many" part in a one-to-many association?

I have a standard one-to-many association in two models:
class User < ActiveRecord::Base
has_many :messages
end
class Message < ActiveRecord::Base
belongs_to :user
end
How do I query for messages that are last for users with name "John"?
I know I can use a joins to filter messages by user attributes. I am also convinced that the "last for that user" part of the query can be done with what is called an sql sub-query, but how exactly it is done is beyond me.
Message.joins(:user).where(users: {name: "John"}).<subquery?>

many to many relation in Rails

I take a look at these 2 blog posts and feel a little obscure about has_many relationship in RoR:
In this one: http://kconrails.com/2010/01/16/many-to-many-relationships-in-ruby-on-rails/ , in "has_many :through" section, the author said the migration files for join table Contributions is like:
class CreateContributions < ActiveRecord::Migration
def self.up
create_table :contributions do |t|
t.references :artist
t.references :song
t.string :instrument
t.timestamps
end
...........
So the migration of join table will reference to 2 table Artist and Song using t.references :artist and t.references :song. And if we want to access an attribute like the instrument artist play for that song, we can access it using join table Contributions.
In the second post: http://kconrails.com/2010/01/29/has_and_belongs_to_many-associations-in-ruby-on-rails/. Section: "has_many :through", author introduce "full-fledged table" named Categorizations. The migration is like below:
class CreateCategorizations < ActiveRecord::Migration
def self.up
create_table :categorizations do |t|
t.integer :category_id
t.integer :item_id
t.timestamps
end
...........
So the migration of full-fledged table will reference to 2 table Category and Item by t.integer :category_id and t.integer :item_id. And we can only access the attributes specific to something that both related to 2 attributes of this table like the timestamps, can we add more attributes (something like t.string :instrument) to the model as above?
For example, If i have many-to-many relation models like Manufacturer and Product, I want to keep track of the Price, I should put it into full-fledged table, right? But if I want to just add one attribute but do not keep track of it like Original_From (show where the product was produced), I only need to put into join table?
Can I generalize it this way: when the number of attributes are limited and do not important to keep track, use the join table. When the number of attributes are many and we want to keep track, we will use full-fledged table. Is it correct?
What are the differences when we access the data? Like if i want to access the Price or the Location in the example above?
I still not clear about the different between full-fledged table and join table. Please give me some ideas. Thank you very much!
I think you have this mostly right. has_and_belongs_to_many (HABTM) on both models of a many-to-many relationship assumes a simple join table between (no id, no timestamps, just foreign keys to each of the tables' primary keys.)
If the relationship itself (the join table) is "useful" -- something you would want to reference in your class, in particular because it has a field of its own, you would define it as its own model and use has_many :through => to define the relationship on both sides, with belongs_to in the new model. This is the "full fledged" option.
Mostly, you'll get the same methods with either option (e.g. you'll get a locations method on Price, and a prices method on Location). But certainly the HABTM option is more limited.
For example, I found recently that the accepts_nested_attributes_for method doesn't work with simple HABTM, but works fine with has_many :through.
I think this Rails guide does a really good job of describing when to use one versus the other.
(And it looks like the coming Rails 4.0 has much better support for HABTM and a few new features for dealing with one-to-many and many-to-many relationships :-)

What is and examples of using data type - References

i wanted to know about the data type references and some examples of how/why it would be used on a website. If their is a difference when using Ruby-on-Rails, i tagged it just in case. I am new at programming and it would help tremendously to explain everything in layman's terms so i can slowly build my way up to being a computer wiz.
Appreciate the trouble in helping me, thanks.
I'm taking a guess that you're referring to t.references :associated_model in a migration?
Suppose two models, Post and Author.
class Post < ActiveRecord::Base
belongs_to :author
end
class Author < ActiveRecord::Base
has_many :posts
end
Your migration contains:
create_table :posts do |t|
t.references :author
end
This will create the author_id column on the posts table with the integer datatype.
In migrations, t.belongs_to is an alias for t.references and matches the naming used to set up the associations in your models.
It is not a real datatype, it is the rails shorthand for creating a foreign key in the table, which is by default an integer.
When you call t.references :widgets in your migration, it actually creates an integer column called widget_id
You may want to read through the Rails Migrations Guide to learn more about how database and migrations are handled in rails.