authentication with join tables for multiple username or emails - ruby-on-rails

I need help on devise authentication (https://github.com/heartcombo/devise) about logins.
My db design has 3 tables that need 2 joins, so it can use any of a user's emails for login, using a single password.
profiles table
id
name
emails table
profile_id - foreign key from profiles table using has many
email
users table
profile_id - foreign key from profiles table using one-to-one relationship
encrypted_password
At the moment, I can only set 1 join in conditions.

You didn't provide any details except that 2 tables have a FK to a third. So I cannot provide much detail in response. But joining all 3 is a trivial exercise. it'll come in the format of:
select
from profiles p
join emails e on e.profile_id = p.profile_id
join users u on u.profile_id = p.profile_id
where e.email = <email_address_parameter>
and encryption_routine(<password_value_parameter>) = u.encrypted_password;

Assume you have the correct model relations.
if(email = Email.find_by(email: params[:email])).present?
if(email.profile.users.where(encrypted_password: encrypt(params[:password])).count > 0)
# login success
else
# wrong password
end
else
# email not exists
end

Related

rails activerecord - distinct counter cache implementation

I have these tables:
broadcast
id
name
email
id
broadcast_id
user_id
subject
email_open
id
email_id
user_id
I want to keep a count of the email_open records in my broadcast table.
Is the most efficient way of doing this by having a broadcast_id in my email_open table? If it is.. then I know I can just do this in my email_open model:
belongs_to :broadcast, counter_cache: => true
Then, I add a email_open_count to my broadcast table... but I'm wondering if there's a way to do it without doing this.
Also, multiple users can have repeated records in email_open.. how do I make the count be of distinct user_id?
For example, user_id 1 can open an email 5 times but I just want the email_open_count to be 1.
Thanks
So as per your example, if user_id 1 can open an email 5 times but You just want the email_open_count to be 1.You can the same user_id to email or whichever table you want only if that user opens an email but if the user has already open don't save it again.

Clean and concise way to find active records that have the same id as another set of active records

I have a table called shoppers and another table called Users. I have a shopper_id which is the foreign key in the Shoppers table and refers to the primary key id in the Users table.
I ran a query called #shoppers = shoppers.where("some condition")
This allowed me to get a set of shoppers who satisfy the condition. Next I would like to select those Users who have the same id as the shopper_id as the individual objects in #shoppers.
I know I could do this by writing a loop, but I am wondering if ruby on rails allows me to write a Users.where condition that can help me obtain the subset of user objects with the same id as shopper_id arranged in ascending order by the name field in the Users table.
Any ideas?
Try this.
#shoppers = Shopper.where("some condition")
#users = User.where(id: #shoppers.collect(&:shopper_id)).order('name asc')

DatingApp programming: ActiveRecord association for finding Users where there are no Approvals or one 1 way approvals

I'm building a dating-style app where Users can approve other users.
I use an Approval model to track these relationships. Each Approval has a user_id and an approved_id -- the User id of the approved User. It also has rejected_at, a datetime for indicating one User has rejected another.
To present eligible Users to the current_user, I must query for Users for which there are either
No Approval relationship
Approval relationship only with the approved_id as the current_user.id (meaning the eligible User approves the current_user but there's no relationship the other way around
Exclude Users that have an Approval with a non-nil rejected_at attribute where approved_id is the User or user_id is the current_user.
How can I craft an ActiveRecord query to find eligible Users? I understand I can do a joins on Approval but I also want to account for there being NO Approval relationship between Users! I'm thinking it may make more sense to just make 2 separate queries but I'd like to know if it's possible to combine into one..
The behavior you want is a LEFT OUTER JOIN, which will include rows from users whether or not there are any matching rows in approvals. That way, you get either a User that has issued no Approval about your target User, or one who has and we can filter for rejection.
The query would look like
-- Looking for users who either have no opinion or have approved user 1
SELECT *
FROM users
LEFT OUTER JOIN approvals
ON users.id = approvals.user_id
AND approvals.approved_id = 1 -- Filter for only approvals of user 1
WHERE users.id != 1 -- Ignore user 1
AND (
approvals.approved_id IS NULL -- Approving user has no opinion of user 1
OR approvals.rejected_at IS NULL -- Approving user has not rejected user 1
)
;
In pieces,
users LEFT OUTER JOIN approvals considers all users, even if they have no approvals
ON users.id = approvals.user_id pairs users with approvals they created
ON ... AND approvals.approved_id = 1 considers only approvals for User 1
WHERE users.id != 1 considers only other users
WHERE ... approvals.approved_id IS NULL takes users that have not created any approvals pertaining to User 1.
WHERE ... approvals.rejected_at IS NULL takes users that did create an Approval for User 1, and it was not a rejection
ActiveRecord doesn't do anything particularly pretty when we translate this, but it works:
class User < ActiveRecord::Base
def eligible_partners
User.joins( # Join like we did before
<<-SQL
LEFT OUTER JOIN approvals
ON users.id = approvals.user_id
AND approvals.approved_id = #{self.id}
SQL
).where.not(id: id) # Ignore ourselves
.where( # Filter for no approvals or a positive approval
<<-SQL
approvals.approved_id IS NULL
OR approvals.rejected_at IS NULL
SQL
)
end
end
If you want something more readable, you can gather up the IDs of every User that has rejected a given person, and then get all of the other Users. On one hand, it's two queries; on the other, it may be less expensive than a JOIN once your tables get big, and it's way easier to understand.
def other_eligible_partners
rejecter_ids = Approval
.where(approved_id: id) # About ourselves
.where.not(rejected_at: nil) # Rejected us
.pluck(:user_id)
User.where.not(id: rejecter_ids + [id]) # People who didn't reject us
end

Rails outer join, left join or some other join

Table selections
user_id
profile_id
Table profile
user_id
Table user
user has_one profile
profile belongs_to user
user has_many selections
How can I select ALL profiles that are NOT in selections table for a user?
For a pure SQL method, which is practically always the fastest way of doing things with data, I would go with:
select *
from profiles
where id not in (
select profile_id
from user_profiles
where user_id = #{self.id})
ActiveRecord syntax does better with joins, but I'd be inclined for simplicity and readability to keep it as:
Profile.where("id not in (
select profile_id
from user_profiles
where user_id = ?)", self.id)
Might be a round about way with extra iteration. But one that came to my mind.
Profile.all.reject{|pro| pro.selections.nil?}
ids = Selection.where(user_id: self.id).map{|x|x["user_id"]}.uniq
Profile.where(['id not in (?)', ids])
David answer is the most correct of all answers, though I wanted to contribute on a bit of a tuning. If You are using postgres than this post is worth reading:
http://explainextended.com/2009/09/16/not-in-vs-not-exists-vs-left-join-is-null-postgresql/
Basically the gist of what that article says is, that are 3 usual ways to do it:
NOT IN (already suggested by David)
NOT EXISTS
LEF JOIN and checking fro NULLs
The author does some research and comes up with conclusion that second and third produce the same explain plan, which means that both are virtually the same.
Revising Davids example it would look like this
Profile.where(
"
NOT EXISTS (
select 1
from selections
where selections.user_id = ? AND selections.profile_id = profiles.id)
", id) # no need to use self when calling instance methods

How to model this classes/database tables?

I have to model classes and database tables for a "User" entity.
This "User" entity have this properties:
User
Name
Age
Gender
Email
But I have 2 user's type: "Paid users" and "Free users". Each one have his own properties:
Paid User
Fee
Rate
Free User
Expiration Date
Level
"Paid User" and "Free User" are different, but both are "User".
I'm using ASP.NET MVC, NHibernate and Fluent Maps.
What is the best way and the right design pattern to model this classes and database tables?
Thank you very much!
I often find the best approach for this is to use an inheritance model. All common fields go into the User table. You create an entry in User first, and then use the resulting UserID to create additional entries in Paid User or Free User as necessary.
User
UserID
Name
Age
Gender
Email
Paid User
UserID <-- FK to User
Fee
Rate
Free User
UserID <-- FK to User
Expiration Date
Level
Then you can select (or create a VIEW) like this:
select u.UserID, U.Name, ...
pu.Fee, pu.Rate,
fu.ExpirationDate, fu.Level,
case when pu.UserID is null then 0 else 1 end as IsPaidUser,
case when fu.UserID is null then 0 else 1 end as IsFreeUser
from User u
left outer join PaidUser pu on u.UserID = pu.UserID
left outer join FreeUser fu on u.UserID = fu.UserID
Note: The above schema is pretty naive and doesn't handle cases where a user could be a free user, then a paid user, then a free user again. How you design for this really depends on your application and business rules.
Assuming that in your Object model User would be considered abstract consider the following User table definition:
User
ID
Name
Age
Gender
Email
Type <-- Free/Paid/etc
Paid User
ID
UserID <--- FK to User table
Fee
Rate
Free User
ID
UserID <--- FK to User table
Expiration Date
Level
This will allow you to query in this fashion:
Select * from User where Type = Free
What operations for theese user types?
Any design should start from operations then data structures, not conversely.

Resources