How to get the arel table of a habtm association? - sql

I have two ActiveRecord models which have a HABTM association.
I want to write a scope to get the orphan records using Arel.
My problem is that I couldn't find a method to retrieve the arel_table of the association. Since the relation is HABTM, there is no model to call arel_table on.
I have the following now (which works), but I make a new arel table with the name of the join table (retrieved by using the reflect_on_association method).
scope :orphans, lambda {
teachers = arel_table
join_table = Arel::Table.new(reflect_on_association(:groups).options[:join_table])
join_table_condition = join_table.project(join_table[:teacher_id])
where(teachers[:id].not_in(join_table_condition))
}
This produces the following SQL:
SELECT `teachers`.*
FROM `teachers`
WHERE (`teachers`.`id` NOT IN (SELECT `groups_teachers`.`teacher_id`
FROM `groups_teachers` ))
So is there any better way to retrieve the arel_table instead of making a new one?

Unfortunately, I believe your solution is pretty much the cleanest there is right now and is, in fact, what the association itself does internally when instantiated:
https://github.com/rails/rails/blob/46492949b8c09f99db78b9f7a02d039e7bc6a702/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb#L7
I believe the reflection.join_table they use vs reflection.options[:join_table] is only in master right now though.

If you know the name of the association and therefore the join table, which in this case it looks like you do, you should be able to call:
Arel::Table.new(:groups_teachers)
In my testing that returns Arel table of a habtm association. Thanks to this answer for pointing this out to me.

Related

Using few columns from associated table with a SQL Join

Whenever I want to avoid NxN queries, I use includes in Rails. But sometimes the included table might have large number of fields.
So to avoid performance issues what I do is, I do a join and I select only required fields from the associated table.
But this does not feel right at all. I feels like violating something although Im not sure what.
I understand that when we do includes, we cannot specify the select clause. How do we achieve this without doing a join and polluting(?) the objects.
There is no "nice" rails way to do this.
Like you said it isn't possible and also not usefull to combine includes and select because ActiveRecord::Base wraps every query to an object. This whould mean that loading only id and name and leaving out description and user_id for example whould bring you in a situation where you might lose the content of description and user_id when saving the incomplete object to the database.
So you can only do this using plain sql and leave out the object mapping of ActiveRecord so you don't get in danger of losing data. You can also do thisthrough the database connection established by ActiveRecord like this:
ActiveRecord::Base.connection.execute("SELECT id,name FROM table_name WHERE <conditions>").each do |row|
row["id"] #access id of each result
end
ActiveRecord::Base.connection contains a handle for the connection established by using the environment access data provided in your config/database.yml so you can use the same database connection model independent.

How to make ORM (ActiveRecord) Models for union queries in Rails

I have an application that has some basic entities
Posts
posts have:
Likes
Comments
and Ratings
I then have an SQL view to query for all three. With that I have a model called something like PostActivityView. A post has an activity view so I can call
#post.activity_view
which returns a collection of the appropriate values (from Likes, Comments, and Ratings). This all works correctly.
My issue is that this returns a collection of hashmaps, not Comments, Likes, and Ratings. This makes sense because my view is creating a new "with PostEvents as (...)" result. My question: is there a way to generalize these results and represent them with an ActiveRecord object?
Likes, Comments, and Ratings have different attributes so I do some aliasing in the view (comment's have comment.body for text and Ratings can have rating.comments for text so when needed I rename something like review.comments to .body). So my results all have the same attributes. It seems like I should be able to make an ActiveRecord object like PostEvent which just has the aliased columns. Is this possible?
I don't know how to do what you're describing. However ,do you really need to store them in separate tables? You could keep them all in a single table and use single table inheritance (http://api.rubyonrails.org/classes/ActiveRecord/Base.html#label-Single+table+inheritance) to have separate classes (Likes, Comments, or Ratings) for each type of thing a particular row represents. Then the common stuff could sit up in the parent class, and the stuff specific to the more granular things could go into the descendant classes.
It sounds like your situation is the opposite of that and you're combining separate tables into a single union. I suspect that'd be very difficult to implement in ActiveRecord itself as different databases have different rules for how and when the contents of a database view may be modified (i.e., if you could somehow create an AR class that referenced your view the way you're proposing, what would happen when you call save?)
It sounds like you've gone down the path of providing a view to make it convenient to retrieve all of these objects in one set as a single type of object, when your requirement is really to bring back different objects.
Based on that I'd question the use of the view at all. I'm not anti-view you understand -- we use them quite a lot for producing read-only reports in our application for performance reasons -- but if you need the rows to be returned as their proper object type then I'd retrieve them separately as Likes, Comments, and Ratings.
First solution would be to use the gem scenic and create an activity_views view by using a union query:
create view activity_views
as (
select ...
from likes
union
select ...
from comments
union
select ...
from rating
)
your data need to be homogenous of course.

finding Post associated with both of two Categories in Rails 3 without custom SQL

I'm working on a Rails 3 application that has (for the sake of this question) Posts linked to multiple Categories and vice versa through has_and_belongs_to_many associations:
Post < ActiveRecord::Base
has_and_belongs_to_many :categories
end
Category < ActiveRecord::Base
has_and_belongs_to_many :posts
end
I'm trying to figure out how to write an ActiveRecord (or ARel) finder that retrieves all Posts where each Post is linked to both of two Categories. I understand the SQL I'm ultimately trying to generate (two INNER JOINS with aliases to be able to distinguish each one for the matching on each of the two Categories), but so far I haven't figured out a way to create the query without resorting to raw SQL bits.
The reason avoiding custom SQL is so important in this case is that the code I'm writing is generic and heavily data-driven, and it needs to mix with other filtering (and sorting) qualifiers on the query for Post objects, so I can't just hard code either method calls on Post (e.g. to access the collection of Categories) or custom SQL that might not mix well with the SQL generated by the other filters.
I'm open to switching to using a join model (has_many :through) if that somehow makes things easier, or even looking at other ORM options (DataMapper, Mongoid, etc.), but that seems like a huge change just to get something so basic working.
I'm stunned that this isn't easier/more obvious in ActiveRecord/ARel, but maybe I just don't know the magic keywords to search to find the answer. The documentation for ARel is also surprisingly slim, so I'm at a loss. Any help would be much appreciated!
After quite a bit more Googling, I found these two articles (from 2006!) that eventually lead me to the correct answer in ActiveRecord/ARel:
http://blog.hasmanythrough.com/2006/6/12/when-associations-arent-enough
http://blog.hasmanythrough.com/2006/6/12/when-associations-arent-enough-part-2
The (nearly) SQL-free code that produces what I'm looking for is a pretty clever use of the GROUP BY and HAVING operators in SQL:
Post.joins(:categories).where("categories.name" => ['catA','catB']).group('posts.id').having('COUNT(posts.id) = 2')
Basically, it finds all Posts associated with any of the given Categories (including duplicates if, as we hope, there are Posts that match multiple Categories), groups that list by the id field on Post, then trims the results down to only include groups that have exactly the number of matches we want.
I haven't tried mixing this code with my filters on other fields on Post, but I'm pretty sure it'll work.

SQL call in Rails, return object that isn't defined by a model

I know how to call SQL to select data from already defined Models, like so:
Friend.find_by_sql(["...."])
My question is what do I do if I need information that isn't defined by a model?
I have a Meal table, Friend table, and Component table. Components make up Meals, and Friends can be allergic to Components. I have a SQL command (which I can't post here due to confidentiality reasons, but the implementation isn't that relevant anyway) that returns a friend_id and component_id, given a Meal. That is, it returns a list of rows (with two columns each, friend_id and component_id) telling me which Friends are allergic to which Components in a given Meal. But I don't know how to store this in a variable in Ruby and access that information.
To give some pseudocode to give you an idea of what I want to do:
#allergies_for_a_meal = ....<INSERT SQL QUERY HERE>...
#friends_who_are_allergic = Friends.find_by_id(#allergies_for_a_meal.friend_id)
Can someone give me an idea of the proper syntax for this?
If you're inside an ActiveRecord::Base subclass (such as a model) then you'll have access to the current database connection through connection; the connection has useful methods like select_rows:
a = connection.select_rows('select id from ...').map { |r| r[0].to_i }
If you're not inside a model class then you really shouldn't be directly messing with the database but you can use ActiveRecord::Base.connection if you must.
You can create a SQL View in your database, then create a read-only ActiveRecord model to read data from it.
That way:
your complex sql is stored in the dbms (and parsed only once).
you can use all of the usual ActiveRecord accessors and finders to access the data
you can add additional Find constraints to the View's SQL
Note that some Views can be written to, depending on the View's SQL, DBMS, engine, etc. But I recommend against that.

Loading relations in linq2entities automatically

When i have a relation between two entities in my model:
[GroupMember] (*) ----- (1) [User]
and tries to select items from this relation with LINQ:
From entity in _user.GroupMember select entity
I always get an empty result unless I load the relation first with following statement:
_user.GroupMember.Load()
Is there a way to avoid loading the relations like this?
If you have cascading relations, you can handle them with .Include("GroupMember.AnotherTable.YetAnotherTable") which is a little nicer than having to do chained Include calls.
I just realized that when i load the User from the database, I can use Include to load GroupMember with the User like this:
Users=from entity in db.User.Include("GroupMember") select entity
But if I have several relations and maybe wants to access relations on the relations, this gets very ugly.
So I am still looking for a better/nicer solution to my issue.