Querying multiple related records in Rails
up vote
4
down vote
favorite
I have an Account
, User
and Group
model. All User
and Group
records belong to an Account
, so it is possible for both users and groups to be related to various accounts.
Basically, I have some really hacky, pieced together SQL queries that lookup all Account
records related to a given User
or Group
record through various
associations.
Even though it's fast and only requires a single query for everything to work, I really hate the way it's written.
I was wondering if there is any way I could do this more programatically with something like arel
, or if there was a better approach. I also have some security concerns about the code.
class Account < ActiveRecord::Base
belongs_to :accountable, touch: true, polymorphic: true
CLAUSE_FOR_RELATED = '"accounts"."accountable_type" = '%s' AND "accounts"."accountable_id" IN (%s)'.freeze
def self.related_to_user(user)
groups = user.groups.select('"groups".id')
friends = user.following_friendships.select('"friendships".following_id')
queries =
queries.push ['Group', groups.to_sql]
queries.push ['User', friends.to_sql]
queries.push ['User', user.id]
self.related_to(queries)
end
def self.related_to_group(group)
queries = [
['User', group.members.select('"users".id').to_sql]
]
self.related_to(queries)
end
def self.related_to(queries)
clauses = queries.map do |clause|
sprintf(CLAUSE_FOR_RELATED, clause[0], clause[1])
end
self.where(clauses.join(" OR "))
end
end
ruby ruby-on-rails
bumped to the homepage by Community♦ 6 mins ago
This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.
add a comment |
up vote
4
down vote
favorite
I have an Account
, User
and Group
model. All User
and Group
records belong to an Account
, so it is possible for both users and groups to be related to various accounts.
Basically, I have some really hacky, pieced together SQL queries that lookup all Account
records related to a given User
or Group
record through various
associations.
Even though it's fast and only requires a single query for everything to work, I really hate the way it's written.
I was wondering if there is any way I could do this more programatically with something like arel
, or if there was a better approach. I also have some security concerns about the code.
class Account < ActiveRecord::Base
belongs_to :accountable, touch: true, polymorphic: true
CLAUSE_FOR_RELATED = '"accounts"."accountable_type" = '%s' AND "accounts"."accountable_id" IN (%s)'.freeze
def self.related_to_user(user)
groups = user.groups.select('"groups".id')
friends = user.following_friendships.select('"friendships".following_id')
queries =
queries.push ['Group', groups.to_sql]
queries.push ['User', friends.to_sql]
queries.push ['User', user.id]
self.related_to(queries)
end
def self.related_to_group(group)
queries = [
['User', group.members.select('"users".id').to_sql]
]
self.related_to(queries)
end
def self.related_to(queries)
clauses = queries.map do |clause|
sprintf(CLAUSE_FOR_RELATED, clause[0], clause[1])
end
self.where(clauses.join(" OR "))
end
end
ruby ruby-on-rails
bumped to the homepage by Community♦ 6 mins ago
This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.
Is the SQL tag properly used here? Was looking for SQL to review in your code but found only tiny snippets. No bad karma just seems a bit misleading.
– Phrancis
May 21 '14 at 4:31
add a comment |
up vote
4
down vote
favorite
up vote
4
down vote
favorite
I have an Account
, User
and Group
model. All User
and Group
records belong to an Account
, so it is possible for both users and groups to be related to various accounts.
Basically, I have some really hacky, pieced together SQL queries that lookup all Account
records related to a given User
or Group
record through various
associations.
Even though it's fast and only requires a single query for everything to work, I really hate the way it's written.
I was wondering if there is any way I could do this more programatically with something like arel
, or if there was a better approach. I also have some security concerns about the code.
class Account < ActiveRecord::Base
belongs_to :accountable, touch: true, polymorphic: true
CLAUSE_FOR_RELATED = '"accounts"."accountable_type" = '%s' AND "accounts"."accountable_id" IN (%s)'.freeze
def self.related_to_user(user)
groups = user.groups.select('"groups".id')
friends = user.following_friendships.select('"friendships".following_id')
queries =
queries.push ['Group', groups.to_sql]
queries.push ['User', friends.to_sql]
queries.push ['User', user.id]
self.related_to(queries)
end
def self.related_to_group(group)
queries = [
['User', group.members.select('"users".id').to_sql]
]
self.related_to(queries)
end
def self.related_to(queries)
clauses = queries.map do |clause|
sprintf(CLAUSE_FOR_RELATED, clause[0], clause[1])
end
self.where(clauses.join(" OR "))
end
end
ruby ruby-on-rails
I have an Account
, User
and Group
model. All User
and Group
records belong to an Account
, so it is possible for both users and groups to be related to various accounts.
Basically, I have some really hacky, pieced together SQL queries that lookup all Account
records related to a given User
or Group
record through various
associations.
Even though it's fast and only requires a single query for everything to work, I really hate the way it's written.
I was wondering if there is any way I could do this more programatically with something like arel
, or if there was a better approach. I also have some security concerns about the code.
class Account < ActiveRecord::Base
belongs_to :accountable, touch: true, polymorphic: true
CLAUSE_FOR_RELATED = '"accounts"."accountable_type" = '%s' AND "accounts"."accountable_id" IN (%s)'.freeze
def self.related_to_user(user)
groups = user.groups.select('"groups".id')
friends = user.following_friendships.select('"friendships".following_id')
queries =
queries.push ['Group', groups.to_sql]
queries.push ['User', friends.to_sql]
queries.push ['User', user.id]
self.related_to(queries)
end
def self.related_to_group(group)
queries = [
['User', group.members.select('"users".id').to_sql]
]
self.related_to(queries)
end
def self.related_to(queries)
clauses = queries.map do |clause|
sprintf(CLAUSE_FOR_RELATED, clause[0], clause[1])
end
self.where(clauses.join(" OR "))
end
end
ruby ruby-on-rails
ruby ruby-on-rails
edited Dec 26 '16 at 21:20
Jamal♦
30.2k11115226
30.2k11115226
asked Apr 3 '14 at 4:05
bschaeffer
1213
1213
bumped to the homepage by Community♦ 6 mins ago
This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.
bumped to the homepage by Community♦ 6 mins ago
This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.
Is the SQL tag properly used here? Was looking for SQL to review in your code but found only tiny snippets. No bad karma just seems a bit misleading.
– Phrancis
May 21 '14 at 4:31
add a comment |
Is the SQL tag properly used here? Was looking for SQL to review in your code but found only tiny snippets. No bad karma just seems a bit misleading.
– Phrancis
May 21 '14 at 4:31
Is the SQL tag properly used here? Was looking for SQL to review in your code but found only tiny snippets. No bad karma just seems a bit misleading.
– Phrancis
May 21 '14 at 4:31
Is the SQL tag properly used here? Was looking for SQL to review in your code but found only tiny snippets. No bad karma just seems a bit misleading.
– Phrancis
May 21 '14 at 4:31
add a comment |
1 Answer
1
active
oldest
votes
up vote
0
down vote
I would try to use as much as possible what is provided by ActiveRecord
. I was thinking to something like:
class Account < ActiveRecord::Base
belongs_to :accountable, touch: true, polymorphic: true
CLAUSE_FOR_RELATED = '"accounts"."accountable_type" = '%s' AND "accounts"."accountable_id" IN (%s)'.freeze
class << self
def related_to_user(user)
group_ids = user.groups.pluck('groups.id')
friend_ids = user.following_friendships.pluck('friendships.following_id')
query = [
compose_clause('Group', group_ids.join(', ')),
compose_clause('User', [user.id, *friend_ids].join(', '))
].join(' or ')
where(query)
end
def related_to_group(group)
member_ids = group.members.pluck('users.id')
where(compose_clause('User', member_ids.join(', ')))
end
private
def compose_clause(accountable_type, ids)
sprintf(CLAUSE_FOR_RELATED, accountable_type, ids)
end
end
end
Now in Rails 5 you could even write it like:
class Account < ActiveRecord::Base
belongs_to :accountable, touch: true, polymorphic: true
class << self
def related_to_user(user)
group_ids = user.groups.pluck('groups.id')
friend_ids = user.following_friendships.pluck('friendships.following_id')
where(accountable_type: 'Group', accountable_id: group_ids).or(
Account.where(accountable_type: 'User', accountable_id: [user.id, *friend_ids])
)
end
def related_to_group(group)
member_ids = group.members.pluck('users.id')
where(accountable_type: 'User', accountable_id: member_ids)
end
end
end
add a comment |
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
0
down vote
I would try to use as much as possible what is provided by ActiveRecord
. I was thinking to something like:
class Account < ActiveRecord::Base
belongs_to :accountable, touch: true, polymorphic: true
CLAUSE_FOR_RELATED = '"accounts"."accountable_type" = '%s' AND "accounts"."accountable_id" IN (%s)'.freeze
class << self
def related_to_user(user)
group_ids = user.groups.pluck('groups.id')
friend_ids = user.following_friendships.pluck('friendships.following_id')
query = [
compose_clause('Group', group_ids.join(', ')),
compose_clause('User', [user.id, *friend_ids].join(', '))
].join(' or ')
where(query)
end
def related_to_group(group)
member_ids = group.members.pluck('users.id')
where(compose_clause('User', member_ids.join(', ')))
end
private
def compose_clause(accountable_type, ids)
sprintf(CLAUSE_FOR_RELATED, accountable_type, ids)
end
end
end
Now in Rails 5 you could even write it like:
class Account < ActiveRecord::Base
belongs_to :accountable, touch: true, polymorphic: true
class << self
def related_to_user(user)
group_ids = user.groups.pluck('groups.id')
friend_ids = user.following_friendships.pluck('friendships.following_id')
where(accountable_type: 'Group', accountable_id: group_ids).or(
Account.where(accountable_type: 'User', accountable_id: [user.id, *friend_ids])
)
end
def related_to_group(group)
member_ids = group.members.pluck('users.id')
where(accountable_type: 'User', accountable_id: member_ids)
end
end
end
add a comment |
up vote
0
down vote
I would try to use as much as possible what is provided by ActiveRecord
. I was thinking to something like:
class Account < ActiveRecord::Base
belongs_to :accountable, touch: true, polymorphic: true
CLAUSE_FOR_RELATED = '"accounts"."accountable_type" = '%s' AND "accounts"."accountable_id" IN (%s)'.freeze
class << self
def related_to_user(user)
group_ids = user.groups.pluck('groups.id')
friend_ids = user.following_friendships.pluck('friendships.following_id')
query = [
compose_clause('Group', group_ids.join(', ')),
compose_clause('User', [user.id, *friend_ids].join(', '))
].join(' or ')
where(query)
end
def related_to_group(group)
member_ids = group.members.pluck('users.id')
where(compose_clause('User', member_ids.join(', ')))
end
private
def compose_clause(accountable_type, ids)
sprintf(CLAUSE_FOR_RELATED, accountable_type, ids)
end
end
end
Now in Rails 5 you could even write it like:
class Account < ActiveRecord::Base
belongs_to :accountable, touch: true, polymorphic: true
class << self
def related_to_user(user)
group_ids = user.groups.pluck('groups.id')
friend_ids = user.following_friendships.pluck('friendships.following_id')
where(accountable_type: 'Group', accountable_id: group_ids).or(
Account.where(accountable_type: 'User', accountable_id: [user.id, *friend_ids])
)
end
def related_to_group(group)
member_ids = group.members.pluck('users.id')
where(accountable_type: 'User', accountable_id: member_ids)
end
end
end
add a comment |
up vote
0
down vote
up vote
0
down vote
I would try to use as much as possible what is provided by ActiveRecord
. I was thinking to something like:
class Account < ActiveRecord::Base
belongs_to :accountable, touch: true, polymorphic: true
CLAUSE_FOR_RELATED = '"accounts"."accountable_type" = '%s' AND "accounts"."accountable_id" IN (%s)'.freeze
class << self
def related_to_user(user)
group_ids = user.groups.pluck('groups.id')
friend_ids = user.following_friendships.pluck('friendships.following_id')
query = [
compose_clause('Group', group_ids.join(', ')),
compose_clause('User', [user.id, *friend_ids].join(', '))
].join(' or ')
where(query)
end
def related_to_group(group)
member_ids = group.members.pluck('users.id')
where(compose_clause('User', member_ids.join(', ')))
end
private
def compose_clause(accountable_type, ids)
sprintf(CLAUSE_FOR_RELATED, accountable_type, ids)
end
end
end
Now in Rails 5 you could even write it like:
class Account < ActiveRecord::Base
belongs_to :accountable, touch: true, polymorphic: true
class << self
def related_to_user(user)
group_ids = user.groups.pluck('groups.id')
friend_ids = user.following_friendships.pluck('friendships.following_id')
where(accountable_type: 'Group', accountable_id: group_ids).or(
Account.where(accountable_type: 'User', accountable_id: [user.id, *friend_ids])
)
end
def related_to_group(group)
member_ids = group.members.pluck('users.id')
where(accountable_type: 'User', accountable_id: member_ids)
end
end
end
I would try to use as much as possible what is provided by ActiveRecord
. I was thinking to something like:
class Account < ActiveRecord::Base
belongs_to :accountable, touch: true, polymorphic: true
CLAUSE_FOR_RELATED = '"accounts"."accountable_type" = '%s' AND "accounts"."accountable_id" IN (%s)'.freeze
class << self
def related_to_user(user)
group_ids = user.groups.pluck('groups.id')
friend_ids = user.following_friendships.pluck('friendships.following_id')
query = [
compose_clause('Group', group_ids.join(', ')),
compose_clause('User', [user.id, *friend_ids].join(', '))
].join(' or ')
where(query)
end
def related_to_group(group)
member_ids = group.members.pluck('users.id')
where(compose_clause('User', member_ids.join(', ')))
end
private
def compose_clause(accountable_type, ids)
sprintf(CLAUSE_FOR_RELATED, accountable_type, ids)
end
end
end
Now in Rails 5 you could even write it like:
class Account < ActiveRecord::Base
belongs_to :accountable, touch: true, polymorphic: true
class << self
def related_to_user(user)
group_ids = user.groups.pluck('groups.id')
friend_ids = user.following_friendships.pluck('friendships.following_id')
where(accountable_type: 'Group', accountable_id: group_ids).or(
Account.where(accountable_type: 'User', accountable_id: [user.id, *friend_ids])
)
end
def related_to_group(group)
member_ids = group.members.pluck('users.id')
where(accountable_type: 'User', accountable_id: member_ids)
end
end
end
edited Sep 27 '17 at 21:16
answered Sep 27 '17 at 21:09
mabe02
1363
1363
add a comment |
add a comment |
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f46139%2fquerying-multiple-related-records-in-rails%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Is the SQL tag properly used here? Was looking for SQL to review in your code but found only tiny snippets. No bad karma just seems a bit misleading.
– Phrancis
May 21 '14 at 4:31