Rails Arel joins with where condition on joined table - mysql

I'm trying to convert the following Rails where clause to use Arel, mostly to take advantage of the or method that Arel provides.
Post model
class Post
belongs_to :user
end
User model
class User
has_many :posts
end
I'm looking for posts posted by Mark.
This is the Rails Query:
Post.joins(:user).where(users: { first_name: 'Mark' })
I need to convert this query with Arel.
Thanks in advance!

This should do it.
# Generate Arel tables for both
posts = Arel::Table.new(:posts)
users = Arel::Table.new(:users)
# Make a join and add a where clause
posts.join(:users).on(posts[:user_id].eq(users[:id])).where(users[:first_name].eq('Mark'))

If you only need Arel for the where part (not for the join), I think this would be a better solution (will wield Activerecord results):
Post.joins(:user).where(User.arel_table[:first_name].eq('Mark'))

Related

Ruby on Rails: "SELECT" MySQL command

I'm writing an application using MySQL. There's a table called "Requests".
That table has a field "user_id" from Table "Users".
I'd like to select all requests from a user_id.
For example:
SELECT * FROM requests WHERE user_id = ("the id I want");
How can I do that using Ruby language, and not an SQL string?
In ActiveRecords its done like this:
Request.where(user_id: ID)
Request.find_by_user_id(USER-ID-YOU-WANT)
Or
Request.find_by user_id: 'USER-ID-YOU-WANT'
documentation here
There are a few different ways, as you can see from #shivam and #amalrik's answers. It also depends on the ORM you're using. If you're using Rails out of the box, you're probably using ActiveRecord.
Probably the most idiomatic way is to have the correct associations on your User and Request models.
in app/models/user.rb:
class User < ActiveRecord::Base
has_many :requests
end
and app/models/request.rb:
class Request < ActiveRecord::Base
belongs_to :user
end
This would allow you to find a user and call its #requests method:
User.find(user_id).requests

Rails 4 ActiveRecord - how to see how is interpreted a database query?

I have these models:
teacher
class Teacher < ActiveRecord::Base
has_many :days
end
day
class Day < ActiveRecord::Base
belongs_to :teacher
end
And running these query:
active_teachers = Teacher.joins(:days).where("teacher.id" => found_teachers.pluck(:teacher_id).uniq, "days.day_name" => selected_day)
What the query (should) does: found_teachers is an array of all teachers with duplications, remove the duplicity and chose only those teachers that have classes on a respective day (selected_day contains a string, Monday for example).
Because the amount of data in the variable active_teachers is so big that I can't manually go record by record (and I am not sure that I built this query properly and it does exactly what I need), I am trying to find out how is this query translated to SQL from ActiveRecord.
Usually I see everything in the terminal where is running server for the Rails app, but as of now, I don't see there this query stated.
So the question is, how can I see how the ActiveRecord query is translated to SQL?
Thank you in advance.
To get details from a query you're typing, you can do:
query.to_sql
query.explain
You can use
ActiveRecord::Base.logger = Logger.new STDOUT
and run your query in rails console. So it prints out the sql queries in the console

Forming of SQL query in Rails Application

I am new to Ruby on Rails. Now I am working on performance issues of a Rails application. I am using New Relic rpm to find out the bottlenecks of the code. While doing this I find something that I cannot figure out. The problem is that here in my Rails application I have used two models A, B and C where model B has two properties: primary key of A and primary key of C like following:
class B
include DataMapper::Resource
belongs_to :A, :key=>true
belongs_to :C, :key=>true
end
Model of A is as follows:
class A
include DataMapper::Resource
property :prop1
...
has n, :bs
has n, :cs, :through => :bs
end
While issuing the following statement a.find(:c.id=>10) then internally it is executing the following SQL query:
select a.prop1, a.prop2,... from a INNER JOIN b on a.id = b.a_id INNER JOIN c on b.c_id = c.id where (c.id=10) GROUP BY a.prop1, a.prop2,....[here in group by all the properties that has been mentioned in select appears, I don't know why]
And this statement is taking too much time during web transaction. Interesting thing is that, when I am executing the same auto generated query in mysql prompt of my terminal it's taking very less amount of time. I think it's because of mentioning so many fields in group by clause. I cannot understand how the query is being formed. If anyone kindly help me to figure this out and optimize this, I will be really grateful. Thank you.
I assume you have you model associations properly configured, something like this:
class A < ActiveRecord
has_many :B
has_many :C, through: :B
end
class B < ActiveRecord
belongs_to :A
belongs_to :C
end
class C < ActiveRecord
has_many :B
has_many :A, through: :B
end
then you could simply call:
a.c.find(10) #mind the plural forms though
You will get better performance this way.

query: has_many where all the child fields are nil

I'm using Rails 3.2 with ActiveRecord and MySQL and I have models with one to many association:
class Author
has_many :books
end
class Book
belongs_to :author
attr_accessible :review
end
I want to find authors that have all the books without review. I tried:
Author.includes(:books).where('book.review IS NIL')
but is obviously didn't work, because it finds authors that have at least one book without review. What query should I use?
SQL is quite simple:
SELECT authors.name, count(books.review is not null)
FROM authors LEFT JOIN books ON (authors.id=books.author_id)
GROUP BY authors.name
HAVING count(books.review) == 0
Translating it to the AR query language may take me some time...
OK, so it seems to look like this:
Author.count('books.review', joins: :books, select: 'name',
group:'name', having: 'count_books_review=0')
As for me SQL looks much less weird then this ;-)
Basing on the WRz answer I prepared my own query:
Author.joins(:books).group('authors.id').having("count(books.reviews)=0")
It's better suited for me, because it returns an AR Relation (and WRz's query returns a Hash).
Try this
Author.joins(:books).where('books.review is null')
edit: This will fetch all the authors with at least one book with no review. I just realized your question is a bit different.
It would be something like this.
Authors.joins(:books).select('authors.*, count(books.id) as
total_books, count('books.review is null')
as books_without_review.group('authors.id').having(total_books ==
books_without_review)
P.S: This is not the exact syntax and it is untested
Try the following code.
class Author
has_many :books
end
class Book
belongs_to :author
attr_accessible :review
end
authors = Author.all.collect do |author|
if author.books.where(:review => nil).size == author.books.size
author
end
end
authors.compact!
After this code, authors will be an array containing all the authors having all the books unreviewed. Also note that I changed the author association in Book model to belongs_to instead of has_one. It is always a good practice to have has_many relation on one side and belongs_to association on the other side.

Rails database query - is it possible to give a column an alias?

I've got to produce a json feed for an old mobile phone app and some of the labels need to be different from my database column names.
I think the most efficient way of doing this would be to do a create an alias at the database level. So I'm doing things like
Site.where( mobile_visible: true ).select("non_clashing_id AS clientID")
which produces the SQL
SELECT non_clashing_id AS clientID FROM `sites` WHERE `sites`.`mobile_visible` = 1 ORDER BY site_name
If I run this query in MYSQL workbench it produces a column with the heading ClientID as I expect, with the required values.
But if I show the object in a rails view I get {"clientID":null},{"clientID":null},{"clientID":null}
What am I doing wrong? Is there a better way of doing this?
This shows how to access the variable
sites = Site.where( mobile_visible: true ).select("non_clashing_id AS clientID")
sites.each do |site|
puts site.clientID
end
I think by default, activerecord loads column definitions from the database. And, it should load value into existing columns only.
Site.columns
I guess you could add one more item to that array. Or you could use the normal query without alias column name, then add alias_attribute like MurifoX did and overwrite as_json method:
class Site < ActiveRecord::Base
alias_attribute :client_id, :non_clashing_id
def as_json(options={})
options[:methods] = [:client_id]
options[:only] = [:client_id]
super
end
end
Try putting this in your model in addition to the database alias:
class model < ActiveRecord::Base
alias_attribute :non_clashing_id, :client_id
...
end