Rails has_and_belongs_to_many with specific key - sql

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.

Related

How to query an STI-driven model using nested associations?

My requirement is to write a query to retrieve records based of a model on STI based on the nested associations of the model.
Here’s what my modeling looks like:
class Loan < ApplicationRecord
belongs_to :borrower
end
class BusinessLoan < Loan
belongs_to :business, inverse_of: :business_loans
end
class HousingLoan < Loan
end
class Borrower < ApplicationRecord
has_many :loans
has_one :address, as: :addressable
end
class Business < ApplicationRecord
has_many :business_loans, inverse_of: :business
has_one :address, as: :addressable
end
class Address < ApplicationRecord
# COLUMN city, :string
belongs_to :addressable, polymorphic: true
end
I would like to write to retrieve the list of all loans whose business or borrower is in a particular city.
Here’s what I have at the moment:
cities = ["New York", "Washington"]
query_string = [
Loan.select(:id).joins(borrower: :address).where(city: cities).to_sql,
BusinessLoan.select(:id).joins(business: :address).where(city: cities).to_sql
].join(" UNION ")
Loan.where(id: Loan.find_by_sql(query_string))
I require the result as an ActiveRecord relation hence the last query
Is there a better way to write this query?
It doesn't look like there is a way to directly achieve this without writing a few new scopes in the associated models. Rails does not permit any operations in scopes that are not possible on the model on which the scopes are initiated.
You can however still solve the problem to yield a single query as follows:
Define from_cities scopes in Borrower and Business as shown below:
scope :from_cities, ->(cities) { joins(:address).where("addresses.city IN (?)", cities) }
Define from_cities in Loan to use these new scopes:
scope :from_cities, ->(cities) do
where(borrower_id: Borrower.from_cities(cities)).
or(where(business_id: Business.from_cities(cities)))
end

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

postgresql: select records where ALL associated records match condition

I have:
class A < ActiveRecord::Base
has_many :abs
has_many :bs, through: :abs
end
class AB
belongs_to :a
belongs_to :b
end
class B < ActiveRecord::Base
has_many :abs
has_many :as, through: :abs
# and has boolean db field :matches
end
So I want to implement an scope for A that retrieves the As where all it's associated Bs matches=true. Normally, I would do something like:
A.joins(:bs).where(bs: { matches: true })
But this will retrieve As where at least one b matches the conditions, not all.
Ideas?
I would instead look for records where there are zero instances of matches: false. I'd probably use a sub-query, something like...
A.joins(:bs).where('(select count(*) from bs where matches = false) = 0')
But there might be a more ActiveRecord way of doing it.

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})

Active Record: How to select records that have zero has_many relations?

I'm using Rails 3, and I have the following:
class Artist < ActiveRecord::Base
has_many :releases
# more code
end
class Release < ActiveRecord::Base
belongs_to :artist
# more codez
end
If I want to select all artists that have NO releases, how would I do this without hardcoding SQL?