How to get columns of belongs_to table? - ruby-on-rails

college.rb
has_many :tenant_colleges
tenant_college.rb
belongs_to :college
I want to get attribute name of college along with all tenant_college attributes
I do
TenantCollege.select("tenant_colleges.*, colleges.name").joins(:college)
But it doesn't give college name

Try eager loading associations, like this
tenant_colleges = TenantCollege.includes(:colleges)
tenant_colleges.each do |tc|
puts tc.college.name
end
EDIT
Like #lcguida suggested its better to go with two separate queries. If you want to get that in a single query use the .select method as we normally do
TenantCollege.joins(:colleges).select("tenant_colleges.*, colleges.name")
OR
TenantCollege.joins("LEFT JOIN colleges ON tenant_colleges.id = colleges.tenant_id")
.select('tenant_colleges.*', 'colleges.name')

I want to get attribute name of college along with all tenant_college
attributes
As I gone through belongs-to-association-reference and has-many-association-reference, There is no build in method available. But its easy with little hack.
class College < ActiveRecord::Base
def get_college_with_tenant_colleges
_ary = {}
_ary[:user] = self
_ary[:tenant_colleges] = self.tenant_colleges
_ary
end
end
But to make sure that the return type of this method is Hash not ActiveRecord. So In order to get the all method of ActiveRecord you need to iterate.
Hope this help you!

I'm curious as to why you wouldn't use raw SQL for something like this, but the following code ought to work:
TenantCollege.joins(:college).pluck("tenant_colleges.*", "colleges.name")

You can achieve this by setting specific name for sql column:
results = TenantCollege.select("tenant_colleges.*, colleges.name AS college_name").joins(:college)
results[0].college_name

Related

ActiveRecord query array intersection?

I'm trying to figure out the count of certain types of articles. I have a very inefficient query:
Article.where(status: 'Finished').select{|x| x.tags & Article::EXPERT_TAGS}.size
In my quest to be a better programmer, I'm wondering how to make this a faster query. tags is an array of strings in Article, and Article::EXPERT_TAGS is another array of strings. I want to find the intersection of the arrays, and get the resulting record count.
EDIT: Article::EXPERT_TAGS and article.tags are defined as Mongo arrays. These arrays hold strings, and I believe they are serialized strings. For example: Article.first.tags = ["Guest Writer", "News Article", "Press Release"]. Unfortunately this is not set up properly as a separate table of Tags.
2nd EDIT: I'm using MongoDB, so actually it is using a MongoWrapper like MongoMapper or mongoid, not ActiveRecord. This is an error on my part, sorry! Because of this error, it screws up the analysis of this question. Thanks PinnyM for pointing out the error!
Since you are using MongoDB, you could also consider a MongoDB-specific solution (aggregation framework) for the array intersection, so that you could get the database to do all the work before fetching the final result.
See this SO thread How to check if an array field is a part of another array in MongoDB?
Assuming that the entire tags list is stored in a single database field and that you want to keep it that way, I don't see much scope of improvement, since you need to get all the data into Ruby for processing.
However, there is one problem with your database query
Article.where(status: 'Finished')
# This translates into the following query
SELECT * FROM articles WHERE status = 'Finished'
Essentially, you are fetching all the columns whereas you only need the tags column for your process. So, you can use pluck like this:
Article.where(status: 'Finished').pluck(:tags)
# This translates into the following query
SELECT tags FROM articles WHERE status = 'Finished'
I answered a question regarding general intersection like queries in ActiveRecord here.
Extracted below:
The following is a general approach I use for constructing intersection like queries in ActiveRecord:
class Service < ActiveRecord::Base
belongs_to :person
def self.with_types(*types)
where(service_type: types)
end
end
class City < ActiveRecord::Base
has_and_belongs_to_many :services
has_many :people, inverse_of: :city
end
class Person < ActiveRecord::Base
belongs_to :city, inverse_of: :people
def self.with_cities(cities)
where(city_id: cities)
end
# intersection like query
def self.with_all_service_types(*types)
types.map { |t|
joins(:services).merge(Service.with_types t).select(:id)
}.reduce(scoped) { |scope, subquery|
scope.where(id: subquery)
}
end
end
Person.with_all_service_types(1, 2)
Person.with_all_service_types(1, 2).with_cities(City.where(name: 'Gold Coast'))
It will generate SQL of the form:
SELECT "people".*
FROM "people"
WHERE "people"."id" in (SELECT "people"."id" FROM ...)
AND "people"."id" in (SELECT ...)
AND ...
You can create as many subqueries as required with the above approach based on any conditions/joins etc so long as each subquery returns the id of a matching person in its result set.
Each subquery result set will be AND'ed together thus restricting the matching set to the intersection of all of the subqueries.

How to write complex query in Ruby

Need advice, how to write complex query in Ruby.
Query in PHP project:
$get_trustee = db_query("SELECT t.trustee_name,t.secret_key,t.trustee_status,t.created,t.user_id,ui.image from trustees t
left join users u on u.id = t.trustees_id
left join user_info ui on ui.user_id = t.trustees_id
WHERE t.user_id='$user_id' AND trustee_status ='pending'
group by secret_key
ORDER BY t.created DESC")
My guess in Ruby:
get_trustee = Trustee.find_by_sql('SELECT t.trustee_name, t.secret_key, t.trustee_status, t.created, t.user_id, ui.image FROM trustees t
LEFT JOIN users u ON u.id = t.trustees_id
LEFT JOIN user_info ui ON ui.user_id = t.trustees_id
WHERE t.user_id = ? AND
t.trustee_status = ?
GROUP BY secret_key
ORDER BY t.created DESC',
[user_id, 'pending'])
Option 1 (Okay)
Do you mean Ruby with ActiveRecord? Are you using ActiveRecord and/or Rails? #find_by_sql is a method that exists within ActiveRecord. Also it seems like the user table isn't really needed in this query, but maybe you left something out? Either way, I'll included it in my examples. This query would work if you haven't set up your relationships right:
users_trustees = Trustee.
select('trustees.*, ui.image').
joins('LEFT OUTER JOIN users u ON u.id = trustees.trustees_id').
joins('LEFT OUTER JOIN user_info ui ON ui.user_id = t.trustees_id').
where(user_id: user_id, trustee_status: 'pending').
order('t.created DESC')
Also, be aware of a few things with this solution:
I have not found a super elegant way to get the columns from the join tables out of the ActiveRecord objects that get returned. You can access them by users_trustees.each { |u| u['image'] }
This query isn't really THAT complex and ActiveRecord relationships make it much easier to understand and maintain.
I'm assuming you're using a legacy database and that's why your columns are named this way. If I'm wrong and you created these tables for this app, then your life would be much easier (and conventional) with your primary keys being called id and your timestamps being called created_at and updated_at.
Option 2 (Better)
If you set up your ActiveRecord relationships and classes properly, then this query is much easier:
class Trustee < ActiveRecord::Base
self.primary_key = 'trustees_id' # wouldn't be needed if the column was id
has_one :user
has_one :user_info
end
class User < ActiveRecord::Base
belongs_to :trustee, foreign_key: 'trustees_id' # relationship can also go the other way
end
class UserInfo < ActiveRecord::Base
self.table_name = 'user_info'
belongs_to :trustee
end
Your "query" can now be ActiveRecord goodness if performance isn't paramount. The Ruby convention is readability first, reorganizing code later if stuff starts to scale.
Let's say you want to get a trustee's image:
trustee = Trustee.where(trustees_id: 5).first
if trustee
image = trustee.user_info.image
..
end
Or if you want to get all trustee's images:
Trustee.all.collect { |t| t.user_info.try(:image) } # using a #try in case user_info is nil
Option 3 (Best)
It seems like trustee is just a special-case user of some sort. You can use STI if you don't mind restructuring you tables to simplify even further.
This is probably outside of the scope of this question so I'll just link you to the docs on this: http://api.rubyonrails.org/classes/ActiveRecord/Base.html see "Single Table Inheritance". Also see the article that they link to from Martin Fowler (http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html)
Resources
http://guides.rubyonrails.org/association_basics.html
http://guides.rubyonrails.org/active_record_querying.html
Yes, find_by_sql will work, you can try this also:
Trustee.connection.execute('...')
or for generic queries:
ActiveRecord::Base.connection.execute('...')

ActiveRelation where statement on child attribute

I have a has_one condition that I'm trying to access but am having a little trouble
Solicitation belongs_to :lead
Lead has_many :solicitations
My first statement grabs all solicitations for a user
#solicitations = current_user.solicitations.includes(:lead)
I can already access the attribute lead.case_type and could just cycle through the relation and put them in their places manually, but I figure their is an easier way.
What I am trying to do is something similar to
#solicitations.where("lead.case_type = ?", "Civil")
I have tried these and receive an unknown column error lead.case_type
Solicitation.all(:conditions => {:lead => {:case_type => 'Civil'}}, :joins => :lead)
The problem is that you are using lead.case_type, but (if you're following Rails' conventions) your table name is leads. This should work:
#solicitations = current_user.solicitations.includes(:lead).where("leads.case_type = ?", "Civil")
You could also use joins for that:
#solicitations = current_user.solicitations.joins(:lead).where("leads.case_type = ?", "Civil")
includes does an outer join, whereas joins does an inner join. Since you're querying the joined table an inner join would be better here.
In where you always have to use the table name (plural), but in includes and joins it depends on the relationship. In this case solicitation belongs to lead, so you have to use :lead (singular). It's a bit confusing, but I hope this clears it up for you.

Join and select multiple column

I'm trying to select multiple columns after doing a join. I couldn't find a way to do so using ActiveRecord without writing SQL between quotation marks in the query (Thing I'd like to avoid)
Exploring Arel, I've found I could select multiple columns using "project", however I'm not quite sure if I should use Arel directly or if there was a way to achieve the same with AR.
These is the code in Arel:
l = Location.arel_table
r = Region.arel_table
postcode_name_with_region_code = l.where(l[:id].eq(location_id)).join(r).on(l[:region_id].eq(r[:id])).project(l[:postcode_name], r[:code])
After running this query I'd like to return something along the lines of:
(Pseudo-code)
"#{:postcode_name}, #{:code}"
Is there a way to achieve the same query using AR?
If I stick to Arel, how can I get the values out of the SelectManager class the above query returns.
Thanks in advance,
Using AR, without writing any SQL and assuming your models and associations are:
models/region.rb
class Region < ActiveRecord::Base
has_many :locations
end
model/location.rb
class Location < ActiveRecord::Base
belongs_to :region
end
You can certainly do:
postcode_name_with_region_code = Location.where(:id=>location_id).includes(:region).collect{|l| "#{l.postcode_name}, #{l.region.code}"}
This will do the query and then use Ruby to format your result (note that it will return an array since I'm assuming there could be multiple records returned). If you only want one item of the array, you can use the array.first method to reference it.
You could also eager load the association and build your string from the result:
my_loc = Location.find(location_id, :include=>:region)
postcode_name_with_region_code = "#{my_loc.postcode_name}, #{my_loc.region.code}"
predicate = Location.where(:id=>location_id).includes(:region)
predicate.ast.cores.first.projections.clear
predicate.project(Location.arel_table[:postcode_name], Region.arel_table[:code])

combine results from two queries and order by created_at? [rails 3]

Looking for a simple method utilizing active record to grab data from two models, combine the data, and then sort the combined output by created_at.
For example:
assume two models, Comment & Like both belongs_to User
return a combined list of #user's comments and likes sorted by date
I know I can do this in SQL but I'd really like an active record solution.
Thanks!
I believe it should be as simple as:
combined_sorted = (User.comments + User.likes).sort{|a,b| a.created_at <=> b.created_at }
How about something like (untested):
class User < ActiveRecord::Base
scope :activities_by_date, :joins(:comments).joins(:likes).order("created_at desc")
end
Then you can do #user.activities_by_date and let the db do all the work

Resources