ROR Relationships - ruby-on-rails

I have 2 models: Purchase and User. User has_many :purchases, and Purchase belongs_to :user. I want to select the following:
How many distinct users have made a purchase in the last 3 months, and also a way to print their email addresses ('Purchase' has a created_at field) (I want to just do this using the console). I'm a little confused as to how to go about it from a ruby perspective (I could do the straight SQL query, but I'd like to figure out how to do it in Ruby).

User.joins(:purchases).where("purchases.created_at > ?", 3.month.ago)
To get distinct user list
User.joins(:purchases).where("purchases.created_at > ?", 3.month.ago).uniq
To get distinct collection of user emails
User.joins(:purchases).where("purchases.created_at > ?", 3.month.ago).uniq.pluck(:email)

Rails 2: Purchase.all(:conditions => ["created_at > ?", 3.months.ago]).user.uniq
Rails 3: Purchase.where(:created_at > 3.months.ago).user.uniq

It always depends on the data structure and how many record u would have.
If that's in a console which is on ur dev environment do whatever u like, otherwise good sql query will perform better.
Select field which are needed, use pluck where possible to get ids.
Use those ids to do a straight query.
To do comparison <> u will have to use sql.
If u only need emails, add a select to the query so you would have 'select email from users;' instead of 'select * from users'.
so:
User.joins(:purchases).where('purchases.created_at > ?', .month.ago).pluck('distinct(email)')
will return you an array of your users' emails
apart from that have u bothered to read this? and tried that?

Purchase.where(:created_at > 3.months.ago).map(&:user).uniq

Related

Retrive records which are not referenced in other table, ActiveRecord query

There are 2 tables : User and Teacher. Teacher.user_id is from User. So, how do I find in a single query, all the users who are not in teachers.
I meant something along the lines :
User.not_in(Teacher.all)
You can use where.not query from ActiveRecord try something like below:
User.where.not(id: Teacher.pluck(:user_id).reject {|x| x.nil?})
Note: used reject method, in case you have nil values in some records.
The other users seem to have neglected the rails 3 tag (since removed based on the approved answer. My answer left for posterity) : Please try this
User.where("id NOT IN (?)",Teacher.pluck(:user_id).join(","))
This will become SELECT * FROM users WHERE id NOT IN (....) (two queries one to get the user_id from teachers and another to get the user(s) not in that list) and may fail based on the size of teacher table.
Other option is an arel table:
users = User.arel_table
User.where(users[:id].not_in(Teacher.select(:user_id).where("user_id IS NOT NULL")))
This should produce a single query similar to
SELECT * FROM users
WHERE id NOT IN ( SELECT user_id FROM teachers WHERE user_id IS NOT NULL)
(one query better performance) * syntax was not fully tested
Another single query option might be
User.joins("LEFT OUTER JOIN teachers ON teachers.user_id = users.id").
where("teachers.user_id IS NULL")
I think you should be able to do something like this
User.where.not(id: Teacher.ids)

Retrieving multiple records by email

I have a class called Membership. There could be multiple records with the same email. To retrieve all Membership records by email, I am creating an index on email and doing:
Membership.where(email: "example#example.com")
How expensive is the above operation? Is there a more efficient way of doing this?
Thanks!
I think the most efficient way is with group query like this:
Membership.select(:email).group(:email).having("count(*) > 1")
This will generate following query
SELECT "memberships"."email" FROM "memberships" GROUP BY "memberships"."email" HAVING (count(*) > 1)
That should work on PG and MySQL.
Hope it helps
EDIT
if you want to see duplicates, you can do this (put a count at the end)
Membership.select(:email).group(:email).having("count(*) > 1").count
This will give a collection with each email and number of duplicates listed like this:
{ 'some#duplicate.com' => 2, ...etc}

Rails 3 Comparing foreign key to list of ids using activerecord

I have a relationship between two models, Registers and Competitions. I have a very complicated dynamic query that is being built and if the conditions are right I need to limit Registration records to only those where it's Competition parent meets a certain criteria. In order to do this without select from the Competition table I was thinking of something along the lines of...
Register.where("competition_id in ?", Competition.where("...").collect {|i| i.id})
Which produces this SQL:
SELECT "registers".* FROM "registers" WHERE (competition_id in 1,2,3,4...)
I don't think PostgreSQL liked the fact that the in parameters aren't surrounded by parenthesis. How can I compare the Register foreign key to a list of competition ids?
you can make it a bit shorter and skip the collect (this worked for me in 3.2.3).
Register.where(competition_id: Competition.where("..."))
this will result in the following sql:
SELECT "registers".* FROM "registers" WHERE "registers"."competition_id" IN (SELECT "competitions"."id" FROM "competitions" WHERE "...")
Try this instead:
competitions = Competition.where("...").collect {|i| i.id}
Register.where(:competition_id => competitions)

Complex SQL in Rails

How do I form the following query using active record?
SELECT c.*
FROM `course_enrollments` ce JOIN courses c ON ce.course_id = c.id
WHERE ce.created_at
BETWEEN '2000-01-01' and '2012-01-01' [AND ANOTHER POSSIBLE CONDITION]
GROUP BY c.id
I want to be able to do something like: (I know the below is not correct, but I just want to show a general example)
courses = Course.joins(:course_enrollments).where('course_enrollments.created_at' => params[:start_date]..params[:end_date]).group('courses.id')
if some_condition
courses = courses.where(:some_field => 1)
end
The following should get you on the way
Course.joins(:course_enrolements).
where("course_enrolements.created_at between '2000-01-01' and '2012-01-01'").
group("courses.id").
where(MORE CONDITIONS)
use .to_sql to analyze output
Take a look at this Railscast. There are quite a number of ways to do the same elegantly esp. to address your [AND ANOTHER POSSIBLE CONDITION] concern. Also take a look at Squeel gem if its not suggested in the Railscast.

In Ruby on Rails, how do I query for items that have no associated elements in a has_many :through relationship

I have a Contact model, and a User model, and a join table and each is HABTM more or less.
How can I query for the Contacts that have no users assigned to them? Driving me nuts.
Thanks
IMHO you should do a raw SQL query something along the lines of....
select c.*
from contacts c left join contacts_users cu on c.id = cu.contact_id
where cu.contact_id is null
I don't know of any pretty ORM-specific way to do it. Obviously you'll want to tailor the query to use the actual fields from your table.
I believe this thread is looking for the same thing right?
Want to find records with no associated records in Rails 3
If I understand you correctly, then I think it could be something like:
Contact.includes(:jointablenames).where( :jointablenames => {:contact_id => nil } )

Resources