SQL How to query a has_many_through relationship - sql

I have three models that have a has_many_through relationship like this:
class Tag < ActiveRecord::Base
has_many :tag_workspaces
has_many :workspaces, through: :tag_workspaces, dependent: :destroy
end
class Workspace < ActiveRecord::Base
has_many :tag_workspaces
has_many :tags, through: :tag_workspaces, dependent: :destroy
end
class TagWorkspace < ActiveRecord::Base
belongs_to :tag
belongs_to :workspace
end
I need to write a query using sql language to get all the tags that belong to a specific workspace.
So far my query looks like this:
select '0' as "value", 'Seleziona un Tag' as "name"
union all
SELECT name AS "value",
name
FROM tags
But I need to select only the tags that belong to workspace 1, for example. How could I accomplish that? Thank you so much!

In SQL, this would be ...
select t.*
from tags t
join tag_workspaces tw on tw.tag_id = t.id
join workspaces w on w.id = tw.workspace_id
where w.id = 1

Related

Rails has_and_belongs_to_many with specific key

I has two models
class Fellow < ApplicationRecord
has_and_belongs_to_many :skills
end
class Skill < ApplicationRecord
has_and_belongs_to_many :fellows
end
One fellow can have some skills, and one skill can be learned by some fellows. So I have third table
class CreateFellowsSkills < ActiveRecord::Migration[5.0]
def change
create_table :fellows_skills, id:false do |t|
t.belongs_to :skill, index: true
t.belongs_to :fellow, index: true
end
end
end
I want to use method: fellow.skills
That invoke such SQL:
SELECT "skills".* FROM "skills" INNER JOIN "fellows_skills" ON "skills"."id" = "fellows_skills"."skill_id" WHERE "fellows_skills"."fellow_id" = $1
The problem: I want to use field skill_id in table skills instead of id, so the query should be such:
SELECT "skills".* FROM "skills" INNER JOIN "fellows_skills" ON "skills"."skill_id" = "fellows_skills"."skill_id" WHERE "fellows_skills"."fellow_id" = $1
I tried to use different options in method has_and_belongs_to_many but the query is still incorrect.
http://cobwwweb.com/why-i-dont-use-has-and-belongs-to-many-in-rails
Instead of habtm, use
class Fellow < ApplicationRecord
has_many :fellows_skills
has_many :skills, through :fellows_skills
end
class Skill < ApplicationRecord
has_many :fellows_skills
has_many :fellows, through: :fellows_skills
end
class FellowsSkill < ApplicationRecord
belongs_to :fellow
belongs_to :skill
end
Furthermore I would suggest naming the third model FellowSkill (dropping the plural on fellow).
Finally I correct my db scheme and call primary key ID, so I don't need apply any changes now.

Sort by a column in JOIN table of many-to-many relationship (rails)

I have a many to many relationship between category and post.
The join table is category_post_relationships.
class Post < ActiveRecord::Base
has_many :categories, through: :category_link_relationships
has_many :category_post_relationships , :dependent => :destroy
end
class Category < ActiveRecord::Base
has_many :posts, through: :category_link_relationships
has_many :category_post_relationships , :dependent => :destroy
end
class CategoryPostRelationship < ActiveRecord::Base
belongs_to :post
belongs_to :category
end
If I have a category, the category can query for all posts by category.posts. And I want to sort these posts by the created_at of the join table category_link_relationships. There can be only one record for each category and post. I want to sort the links by the column created_at of related relationship records.
For example, post 1 was created and associated to category A.
Then, the post 1 was associated to category B.
Post 2 was then created and associated to category B.
category B now has post 1 and post 2. I want to sort post 1 and post 2 by the created_at of the relationships, not the created_at of posts.
Thanks.
class Post < ActiveRecord::Base
has_many :category_link_relationships , :dependent => :destroy
has_many :categories, through: :category_link_relationships
end
class Category < ActiveRecord::Base
has_many :category_link_relationships , :dependent => :destroy
has_many :posts, through: :category_link_relationships
end
and now you can find posts in next way:
#category.posts.joins(:category_link_relationships).order('category_link_relati‌​onships.created_at')
If you will always order this way, you can use a default scope on the join table.
class CategoryLinkRelationship < ApplicationRecord
belongs_to :post
belongs_to :category
default_scope { order(created_at: :asc)}
end
This will be automatically picked up by ActiveRecord. Be careful, if there is also a default scope on Category it will override the sort.

rails select distinct nested associations and fetch those associations

i have the models User, Company, Product, View
class Company < ActiveRecord::Base
has_many :users
end
class Product < ActiveRecord::Base
has_many :views_by_user, -> { where viewable_type: User },
as: :viewable, class_name: "View"
end
class User < ActiveRecord::Base
has_many :viewed, as: :viewer, class_name: "View"
belongs_to :company
end
class View < ActiveRecord::Base
belongs_to :viewable, polymorphic: true
belongs_to :viewer, polymorphic: true
end
What i did with the above is, when a user views product, i save the data in the views
Now i want the list of distinct companies that have looked at my product(via user) and total count for my serializer. what i have done is,
distinct_users = #product.views_by_user
.includes(viewer: [:company])
.joins("left outer join users on views.viewer_id = users.id")
.select("distinct users.company_id, views.*")
but with this, i would have to do something like
distinct_users.will_paginate(...).map(&:viewer).map(&:company)
is there a better way to do it? also if i use distinct_users.count it throws me an error
PG::UndefinedFunction: ERROR: function count(integer, views) does not exist
LINE 1: SELECT COUNT(distinct users.company_id,...
Start from Company if this is the type of record you actually want. You can use merge to combine the conditions on a relation with those from another. Try this:
Company.joins(:users => :viewed).merge(View.where(viewable: #product))
HTH

How to write this ActiveRecord Query using Join instead of subquery in Rails 4

Consider the following:
class User < ActiveRecord::Base
has_many :events
end
class Event < ActiveRecord::Base
belongs_to :user #this user is the event owner
has_many :members
end
class Members < ActiveRecord::Base
belongs_to :user
belongs_to :event
end
Now, I need to list all the members for which current_user is the owner. so I have come up with this:
#members = Member.where event_id: current_user.events
which produces the following query:
SELECT "members".* FROM "members" WHERE "members"."event_id" IN (SELECT "events"."id" FROM "events" WHERE "events"."user_id" = 1)
This works as expected but uses subqueries instead of JOIN. Does anyone know a better way to write this same query?
Add a has_many :through association to your User model:
class User < ActiveRecord::Base
has_many :events
has_many :members, :through => :events
end
Now you can query for all a user's members through the members association:
user.members
The SQL generated will look something like:
SELECT "members".* FROM "members" INNER JOIN "events" ON "members"."id" = "events"."member_id" WHERE "events"."user_id" = 1
Transformed to JOIN syntax (with table aliases to make it shorter and easier to read):
SELECT m.*
FROM events e
JOIN members m ON m.event_id = e.id
WHERE e.user_id = $1
I guess this will work.
Member.joins(:event).where("events.user_id = ?" , current_user.id)
You could do something like :
Member.joins(:event).where(events: {user_id: current_user.id})

Rails 3 has_one with join table

I have the following:
has_many :sports, :through => :user_sports
has_one :primary_sport, class_name: "UserSport", conditions: ["user_sports.primary = ?", true]
has_many :user_sports
When I run this in console:
athlete = Athlete.all.last
athlete.primary_sport
The record that is returned is the record from the join table instead of the record joining the sports table. Any way to return the actual sport from the join?
You might probably do something like this:
class UserSport < ActiveRecord::Base
has_many :athletes
has_many :sports
end
athlete = Athlete.all.last
athlete.primary_sport.sport
Didn't try it by myself, just check and see :)