How to find unassociated records Rails Activerecord - ruby-on-rails

I have these three models
class User < ApplicationRecord
has_many :subscribes
has_many :groups, through: :subscribes
end
class Group < ApplicationRecord
has_many :subscribes
has_many :users, through: :subscribes
end
class Subscribe < ApplicationRecord
belongs_to :user
belongs_to :group
end
I want to query in a clean way, all the groups that the user is not in. How can I do this?

If user has groups association, it means it has group_ids method
Group.where.not(id: user.group_ids)
And also you can explicitly extract such ids
Group.where.not(id: Subscribe.select(:group_id).where(user_id: user.id))

One way to do this would be to use the "where" method in ActiveRecord.
For example:
Group.where("id NOT IN (?)", user.groups.map(&:id))
This would give you all of the groups that the user is not in.
you can also use a subquery for this:
Group.where("id NOT IN (SELECT group_id FROM subscribes WHERE user_id = ?)", user.id)
This would give you the same result as the previous example, but without using the .map method.
"

You can find all the group ids where the user is not present like below:
group_ids = Subscribe.where.not(user_id: user.id).uniq
and then find the group records by querying the groups with these ids:
groups = Group.where(id: group_ids)

Related

Finding siblings through a joins table in Rails

I have models User, Group, and Membership with the following structure:
class User < ApplicationRecord
has_many :memberships
has_many :groups, through: :memberships
end
class Group < ApplicationRecord
has_many :memberships
has_many :users, through: :memberships
end
class Membership < ApplicationRecord
belongs_to :user
belongs_to :group
end
Basically, Membership is the joins table for User and Group.
Given a User user, how can I find its sibling users who all belong to at least one of the same group? That is, something like
user.groups.users # assuming such a line were possible
I'd like to do it in a single query and solely in Active Record, but I'm OK with two queries if that's faster or much more readable. (The DB language is PSQL if that helps as well.)
There are some ways to combine JOINs and sub-selects to get exactly one DB query, try this one:
User
.joins(:memberships)
.where.not(id: user.id)
.where(
memberships: {
group_id: user.memberships.select(:group_id)})
P.S. Don't forget about indexes on all *_id columns to get fast queries.
P.P.S. One more way: 2 sub-selects, 1 DB query. Test which one suits your requirements better:
user_groups_rel = user
.memberships
.select(:group_id)
groups_users_rel = Membership
.select(:user_id)
.where(group_id: user_groups_rel)
User
.where.not(id: user.id)
.where(id: groups_users_rel)
Try the following by using includes:
groups = User.groups.includes(:users)
groups.each do |group|
puts "user = #{group.user}"
end

Need help getting list of queries from Rails ActiveRecord

I currently have three models:
class Match < ApplicationRecord
has_many :rosters, dependent: :destroy
has_and_belongs_to_many :players
end
class Player < ApplicationRecord
has_and_belongs_to_many :matches
end
class Roster < ApplicationRecord
belongs_to :match
end
Let's say I have a specific player's id, say my_player_id. My current database contains several matches, each match has 2 rosters, and each roster has 3 players.
How can I get a list of rosters that belong to matches that belong to a player? (the rosters that have my_player_id and the roster that my_player_id is playing against)
I've tried looking into using joins and where methods but not sure where to go from there. Any answer and explanation of the code would be appreciated!
I think this should work
Roster.joins(match: :players).where('players.id = ?', my_player_id)
Update
If you do .to_sql you will get the equivalent sql query and what it simply does is use your associations to inner join match with rosters first using the foreign_key and then join players to matches and your data is ready. Now you just provide the where clause to fetch data belonging to particular player_id.
Amazingly, following should also work..
class Player < ApplicationRecord
has_and_belongs_to_many :matches
has_many :rosters, through: :matches
end
and now it is as easy as
Player.find_by_id(player_id).rosters
The above should work fine and efficient,
another solution is to break it down to into two queries
match_ids = #player.matct_ids
rosters = Roster.where(match_id: match_ids)
Notice! Rails provides a list of ids for child models
Notice
.where(some_attribute: [ARRAY])takes an array as input

Rails belongs_to association with multiple foreign keys

I'm trying to figure out a way to define a belongs_to association where the target record can be found by one of 4 different columns. The setup:
User
class User < ActiveRecord::Base
has_many :managerships, foreign_key: :employee_id
has_many :managers, through: :managerships
end
Managership
class Managership < ActiveRecord::Base
belongs_to :employee, class_name: 'User'
belongs_to :manager # <-- ideas?
end
The managerships table has 4 columns manager_id, manager_custom_id, manager_email, manager_saml_id that we can use to look up the user's managers. For every Managership, only one of these columns is ever present.
I'm looking for a way to be able to call user.managers (a manager is a User) so that it returns all users where managerships.manager_id = users.id OR managerships.manager_custom_id = users.custom_id OR managerships.manager_email = users.email OR managerships.manager_saml_id = users.saml_id
Many thanks for any ideas!
Although, I would doubt that 4 foreign keys for one relation is a good idea at all, one way to get managers from User is to use method instead of scope. However you won't be able to use #joins, if you do so.
class User
def managers
self.class.joins(:managerships).where('managerships.manager_id = ? OR managerships.manager_custom_id = ? OR managerships.manager_email = ? OR managerships.manager_saml_id', id, custom_id, emd, saml_id)
end
end
in Managership you can do similar thing, declare #manger instead of belongs_to :manager

How to write active record query to details by using and

i have two tables
1)Properties :fields are id, name, propert_type,category_id
2)Admins : fields id, name,mobile,category_id
i want to write an active record to list all properties , where category_id in properties table and category_id in Admins table are equal, according to current_user_id
i am listing this property list by logging as admin.
model relation
class Category < ActiveRecord::Base
has_many :admins,dependent: :destroy
has_many :properties,dependent: :destroy
end
class Admin < ActiveRecord::Base
belongs_to :category
has_many :properties
end
class Property < ActiveRecord::Base
belongs_to :admin
belongs_to :category
end
i wrote active record like this , but i got error,
can anyone please suggest me a solution for this
#properties= Property.where('properties.category_id=?','admins.category_id=?').and('admins.id=?',current_user.specific.id)
With your assosciation,You can use a sub query for getting your result in one line
#properties = Property.where(category_id: Admin.select("category_id").where(id: current_user.id))
As per my understanding current_user is an Admin. So You can search by the category_id of current_user. If I'm right, try this
#properties = Property.where(category_id: current_user.category_id)

Can I create an activerecord association through an activerecord relation (when using the ancestry gem)?

I am using the ancestry gem in my rails project to create a hierarchy of groups. A group can belong to a parent group and can have many children groups. Each group can have many users who belong to a group. The model looks like this:
class Group < ActiveRecord::Base
has_ancestry
has_many :users
end
I would love to be able to get all users for the descendants of a group, something like this:
class Group < ActiveRecord::Base
has_ancestry
has_many :users
has_many :descendants_users, through: :descendants
end
which, of course, doesn't work.
Any suggestions?
Define a method like this in your Group model.
def descendants_users
User.joins(:groups).where(:group_id => self.subtree_ids) # Use descendant ids if you dont't want 'self' users
end
This will perform 1 query to fetch subtree_ids and 1 query to fetch users, and the result is already distinct users set.
May be you just should to define method, that returns all of descendants' users.
So your model could look like this:
class Group < ActiveRecord::Base
has_ancestry
has_many :users
def descendants_users
self.subtree.map(&:users).flatten.compact.uniq
end
end

Resources