merging two relations in Rails
up vote
0
down vote
favorite
This should be easy I think...
A Organisation has an Owner and Members - both are Users.
This is setup by the organisation using class_name: "User", like so:
class Organisation < ApplicationRecord
belongs_to :owner, class_name: "User", foreign_key: "owner_id"
has_many :organisations_users
has_many :members, class_name: "User", through: :organisations_users, source: :user
end
This works but I need an all_members function (or scope) so I can get back both owner and members in one array (or ActiveRecord object). I thought this would be trivial but it's actually not.
I've tried:
def all_members
members << owner
end
this of course is not what I want at all... this adds the owner to the staff every time I call it.
def all_members
[owner, members]
end
this sort of works but returns a nested array which is hard to access properly.
scope :all_members, joins(:members).merge(:owner)
this doesn't work at all. Probably nonsense.
def all_members
members_array = members.dup
members_array << owner
end
This still permanently alters the members to include the owner?!!
Help! (Thanks)
ruby-on-rails ruby activerecord
add a comment |
up vote
0
down vote
favorite
This should be easy I think...
A Organisation has an Owner and Members - both are Users.
This is setup by the organisation using class_name: "User", like so:
class Organisation < ApplicationRecord
belongs_to :owner, class_name: "User", foreign_key: "owner_id"
has_many :organisations_users
has_many :members, class_name: "User", through: :organisations_users, source: :user
end
This works but I need an all_members function (or scope) so I can get back both owner and members in one array (or ActiveRecord object). I thought this would be trivial but it's actually not.
I've tried:
def all_members
members << owner
end
this of course is not what I want at all... this adds the owner to the staff every time I call it.
def all_members
[owner, members]
end
this sort of works but returns a nested array which is hard to access properly.
scope :all_members, joins(:members).merge(:owner)
this doesn't work at all. Probably nonsense.
def all_members
members_array = members.dup
members_array << owner
end
This still permanently alters the members to include the owner?!!
Help! (Thanks)
ruby-on-rails ruby activerecord
1
You can write your method as[*members, owner]
by using splat operator (*
). It would be best to have a relation that will return all members with one query though.
– Marcin Kołodziej
Nov 7 at 15:38
hey, that worked! I think that might be the smallest change ever required on Stack Overflow
– Paul Harker
Nov 7 at 15:44
1
It still does 2 selects, which I'd aim to optimize. If you add your relations betweenorganisation_users
andmembers
to the question, you should be able to get an acceptable answer.
– Marcin Kołodziej
Nov 7 at 15:49
concat (<<) permanently alters an object try using + instead
– engineerDave
Nov 7 at 22:04
add a comment |
up vote
0
down vote
favorite
up vote
0
down vote
favorite
This should be easy I think...
A Organisation has an Owner and Members - both are Users.
This is setup by the organisation using class_name: "User", like so:
class Organisation < ApplicationRecord
belongs_to :owner, class_name: "User", foreign_key: "owner_id"
has_many :organisations_users
has_many :members, class_name: "User", through: :organisations_users, source: :user
end
This works but I need an all_members function (or scope) so I can get back both owner and members in one array (or ActiveRecord object). I thought this would be trivial but it's actually not.
I've tried:
def all_members
members << owner
end
this of course is not what I want at all... this adds the owner to the staff every time I call it.
def all_members
[owner, members]
end
this sort of works but returns a nested array which is hard to access properly.
scope :all_members, joins(:members).merge(:owner)
this doesn't work at all. Probably nonsense.
def all_members
members_array = members.dup
members_array << owner
end
This still permanently alters the members to include the owner?!!
Help! (Thanks)
ruby-on-rails ruby activerecord
This should be easy I think...
A Organisation has an Owner and Members - both are Users.
This is setup by the organisation using class_name: "User", like so:
class Organisation < ApplicationRecord
belongs_to :owner, class_name: "User", foreign_key: "owner_id"
has_many :organisations_users
has_many :members, class_name: "User", through: :organisations_users, source: :user
end
This works but I need an all_members function (or scope) so I can get back both owner and members in one array (or ActiveRecord object). I thought this would be trivial but it's actually not.
I've tried:
def all_members
members << owner
end
this of course is not what I want at all... this adds the owner to the staff every time I call it.
def all_members
[owner, members]
end
this sort of works but returns a nested array which is hard to access properly.
scope :all_members, joins(:members).merge(:owner)
this doesn't work at all. Probably nonsense.
def all_members
members_array = members.dup
members_array << owner
end
This still permanently alters the members to include the owner?!!
Help! (Thanks)
ruby-on-rails ruby activerecord
ruby-on-rails ruby activerecord
asked Nov 7 at 15:30
Paul Harker
548
548
1
You can write your method as[*members, owner]
by using splat operator (*
). It would be best to have a relation that will return all members with one query though.
– Marcin Kołodziej
Nov 7 at 15:38
hey, that worked! I think that might be the smallest change ever required on Stack Overflow
– Paul Harker
Nov 7 at 15:44
1
It still does 2 selects, which I'd aim to optimize. If you add your relations betweenorganisation_users
andmembers
to the question, you should be able to get an acceptable answer.
– Marcin Kołodziej
Nov 7 at 15:49
concat (<<) permanently alters an object try using + instead
– engineerDave
Nov 7 at 22:04
add a comment |
1
You can write your method as[*members, owner]
by using splat operator (*
). It would be best to have a relation that will return all members with one query though.
– Marcin Kołodziej
Nov 7 at 15:38
hey, that worked! I think that might be the smallest change ever required on Stack Overflow
– Paul Harker
Nov 7 at 15:44
1
It still does 2 selects, which I'd aim to optimize. If you add your relations betweenorganisation_users
andmembers
to the question, you should be able to get an acceptable answer.
– Marcin Kołodziej
Nov 7 at 15:49
concat (<<) permanently alters an object try using + instead
– engineerDave
Nov 7 at 22:04
1
1
You can write your method as
[*members, owner]
by using splat operator (*
). It would be best to have a relation that will return all members with one query though.– Marcin Kołodziej
Nov 7 at 15:38
You can write your method as
[*members, owner]
by using splat operator (*
). It would be best to have a relation that will return all members with one query though.– Marcin Kołodziej
Nov 7 at 15:38
hey, that worked! I think that might be the smallest change ever required on Stack Overflow
– Paul Harker
Nov 7 at 15:44
hey, that worked! I think that might be the smallest change ever required on Stack Overflow
– Paul Harker
Nov 7 at 15:44
1
1
It still does 2 selects, which I'd aim to optimize. If you add your relations between
organisation_users
and members
to the question, you should be able to get an acceptable answer.– Marcin Kołodziej
Nov 7 at 15:49
It still does 2 selects, which I'd aim to optimize. If you add your relations between
organisation_users
and members
to the question, you should be able to get an acceptable answer.– Marcin Kołodziej
Nov 7 at 15:49
concat (<<) permanently alters an object try using + instead
– engineerDave
Nov 7 at 22:04
concat (<<) permanently alters an object try using + instead
– engineerDave
Nov 7 at 22:04
add a comment |
2 Answers
2
active
oldest
votes
up vote
1
down vote
accepted
If the order is not necessarily important then we can get this into a single query like so:
If Rails 5
def all_members
# not sure if you could just do
# owner.or(members) since they are both User
# but it seems unlikely due to the join table
User.where(id: self.owner_id).or(
User.where(id: organisation_users.select(:user_id))
)
end
All versions of Rails (less than and including Rails 5) we can use Arel
to build the more complex query
def all_members
user_table = User.arel_table
User.where(
users_table[:id].eq(self.owner_id).or(
users_table[:id].in(
organisation_users.select(:user_id).arel
)
)
)
end
these will both result in the following SQL
SELECT users.*
FROM users
WHERE
(users.id = [YOUR_OWNER_ID] OR
users.id IN (
SELECT organisations_users.user_id
FROM organisations_users
WHERE organisations_users.organisation_id = [YOUR_ORGANISATION_ID]
))
the end result will be an ActiveRecord::Relation
of User
objects.
add a comment |
up vote
1
down vote
If getting an Array back is sufficient, then you can simply use:
def all_members
[owner] + members
end
If you need a relation, then the simplest (but not most efficient) approach would be:
def all_members
User.where(id: [owner] + members.ids)
end
It's not the most efficient as the SQL that gets generated may include a fairly large IN
statement that cannot be efficiently cached by the database parser. Still, if the numbers are in the range of a few dozens, it would be enough even that relation trick.
The second case should beUser.where
not justwhere
and while I haven't tested this theory I might assume that this should be[owner.id] + members.ids
which would now result in 3 queries (fetch owner, fetch member ids, fetch Users where.)
– engineersmnky
Nov 7 at 21:28
Correct, it will result in 3 queries. Reason why I mentioned it's not extremely efficient.
– Simone Carletti
Nov 8 at 22:23
add a comment |
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
accepted
If the order is not necessarily important then we can get this into a single query like so:
If Rails 5
def all_members
# not sure if you could just do
# owner.or(members) since they are both User
# but it seems unlikely due to the join table
User.where(id: self.owner_id).or(
User.where(id: organisation_users.select(:user_id))
)
end
All versions of Rails (less than and including Rails 5) we can use Arel
to build the more complex query
def all_members
user_table = User.arel_table
User.where(
users_table[:id].eq(self.owner_id).or(
users_table[:id].in(
organisation_users.select(:user_id).arel
)
)
)
end
these will both result in the following SQL
SELECT users.*
FROM users
WHERE
(users.id = [YOUR_OWNER_ID] OR
users.id IN (
SELECT organisations_users.user_id
FROM organisations_users
WHERE organisations_users.organisation_id = [YOUR_ORGANISATION_ID]
))
the end result will be an ActiveRecord::Relation
of User
objects.
add a comment |
up vote
1
down vote
accepted
If the order is not necessarily important then we can get this into a single query like so:
If Rails 5
def all_members
# not sure if you could just do
# owner.or(members) since they are both User
# but it seems unlikely due to the join table
User.where(id: self.owner_id).or(
User.where(id: organisation_users.select(:user_id))
)
end
All versions of Rails (less than and including Rails 5) we can use Arel
to build the more complex query
def all_members
user_table = User.arel_table
User.where(
users_table[:id].eq(self.owner_id).or(
users_table[:id].in(
organisation_users.select(:user_id).arel
)
)
)
end
these will both result in the following SQL
SELECT users.*
FROM users
WHERE
(users.id = [YOUR_OWNER_ID] OR
users.id IN (
SELECT organisations_users.user_id
FROM organisations_users
WHERE organisations_users.organisation_id = [YOUR_ORGANISATION_ID]
))
the end result will be an ActiveRecord::Relation
of User
objects.
add a comment |
up vote
1
down vote
accepted
up vote
1
down vote
accepted
If the order is not necessarily important then we can get this into a single query like so:
If Rails 5
def all_members
# not sure if you could just do
# owner.or(members) since they are both User
# but it seems unlikely due to the join table
User.where(id: self.owner_id).or(
User.where(id: organisation_users.select(:user_id))
)
end
All versions of Rails (less than and including Rails 5) we can use Arel
to build the more complex query
def all_members
user_table = User.arel_table
User.where(
users_table[:id].eq(self.owner_id).or(
users_table[:id].in(
organisation_users.select(:user_id).arel
)
)
)
end
these will both result in the following SQL
SELECT users.*
FROM users
WHERE
(users.id = [YOUR_OWNER_ID] OR
users.id IN (
SELECT organisations_users.user_id
FROM organisations_users
WHERE organisations_users.organisation_id = [YOUR_ORGANISATION_ID]
))
the end result will be an ActiveRecord::Relation
of User
objects.
If the order is not necessarily important then we can get this into a single query like so:
If Rails 5
def all_members
# not sure if you could just do
# owner.or(members) since they are both User
# but it seems unlikely due to the join table
User.where(id: self.owner_id).or(
User.where(id: organisation_users.select(:user_id))
)
end
All versions of Rails (less than and including Rails 5) we can use Arel
to build the more complex query
def all_members
user_table = User.arel_table
User.where(
users_table[:id].eq(self.owner_id).or(
users_table[:id].in(
organisation_users.select(:user_id).arel
)
)
)
end
these will both result in the following SQL
SELECT users.*
FROM users
WHERE
(users.id = [YOUR_OWNER_ID] OR
users.id IN (
SELECT organisations_users.user_id
FROM organisations_users
WHERE organisations_users.organisation_id = [YOUR_ORGANISATION_ID]
))
the end result will be an ActiveRecord::Relation
of User
objects.
edited Nov 7 at 19:27
answered Nov 7 at 19:17
engineersmnky
12.8k12037
12.8k12037
add a comment |
add a comment |
up vote
1
down vote
If getting an Array back is sufficient, then you can simply use:
def all_members
[owner] + members
end
If you need a relation, then the simplest (but not most efficient) approach would be:
def all_members
User.where(id: [owner] + members.ids)
end
It's not the most efficient as the SQL that gets generated may include a fairly large IN
statement that cannot be efficiently cached by the database parser. Still, if the numbers are in the range of a few dozens, it would be enough even that relation trick.
The second case should beUser.where
not justwhere
and while I haven't tested this theory I might assume that this should be[owner.id] + members.ids
which would now result in 3 queries (fetch owner, fetch member ids, fetch Users where.)
– engineersmnky
Nov 7 at 21:28
Correct, it will result in 3 queries. Reason why I mentioned it's not extremely efficient.
– Simone Carletti
Nov 8 at 22:23
add a comment |
up vote
1
down vote
If getting an Array back is sufficient, then you can simply use:
def all_members
[owner] + members
end
If you need a relation, then the simplest (but not most efficient) approach would be:
def all_members
User.where(id: [owner] + members.ids)
end
It's not the most efficient as the SQL that gets generated may include a fairly large IN
statement that cannot be efficiently cached by the database parser. Still, if the numbers are in the range of a few dozens, it would be enough even that relation trick.
The second case should beUser.where
not justwhere
and while I haven't tested this theory I might assume that this should be[owner.id] + members.ids
which would now result in 3 queries (fetch owner, fetch member ids, fetch Users where.)
– engineersmnky
Nov 7 at 21:28
Correct, it will result in 3 queries. Reason why I mentioned it's not extremely efficient.
– Simone Carletti
Nov 8 at 22:23
add a comment |
up vote
1
down vote
up vote
1
down vote
If getting an Array back is sufficient, then you can simply use:
def all_members
[owner] + members
end
If you need a relation, then the simplest (but not most efficient) approach would be:
def all_members
User.where(id: [owner] + members.ids)
end
It's not the most efficient as the SQL that gets generated may include a fairly large IN
statement that cannot be efficiently cached by the database parser. Still, if the numbers are in the range of a few dozens, it would be enough even that relation trick.
If getting an Array back is sufficient, then you can simply use:
def all_members
[owner] + members
end
If you need a relation, then the simplest (but not most efficient) approach would be:
def all_members
User.where(id: [owner] + members.ids)
end
It's not the most efficient as the SQL that gets generated may include a fairly large IN
statement that cannot be efficiently cached by the database parser. Still, if the numbers are in the range of a few dozens, it would be enough even that relation trick.
edited Nov 8 at 22:22
answered Nov 7 at 20:23
Simone Carletti
144k34308331
144k34308331
The second case should beUser.where
not justwhere
and while I haven't tested this theory I might assume that this should be[owner.id] + members.ids
which would now result in 3 queries (fetch owner, fetch member ids, fetch Users where.)
– engineersmnky
Nov 7 at 21:28
Correct, it will result in 3 queries. Reason why I mentioned it's not extremely efficient.
– Simone Carletti
Nov 8 at 22:23
add a comment |
The second case should beUser.where
not justwhere
and while I haven't tested this theory I might assume that this should be[owner.id] + members.ids
which would now result in 3 queries (fetch owner, fetch member ids, fetch Users where.)
– engineersmnky
Nov 7 at 21:28
Correct, it will result in 3 queries. Reason why I mentioned it's not extremely efficient.
– Simone Carletti
Nov 8 at 22:23
The second case should be
User.where
not just where
and while I haven't tested this theory I might assume that this should be [owner.id] + members.ids
which would now result in 3 queries (fetch owner, fetch member ids, fetch Users where.)– engineersmnky
Nov 7 at 21:28
The second case should be
User.where
not just where
and while I haven't tested this theory I might assume that this should be [owner.id] + members.ids
which would now result in 3 queries (fetch owner, fetch member ids, fetch Users where.)– engineersmnky
Nov 7 at 21:28
Correct, it will result in 3 queries. Reason why I mentioned it's not extremely efficient.
– Simone Carletti
Nov 8 at 22:23
Correct, it will result in 3 queries. Reason why I mentioned it's not extremely efficient.
– Simone Carletti
Nov 8 at 22:23
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%2fstackoverflow.com%2fquestions%2f53192611%2fmerging-two-relations-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
1
You can write your method as
[*members, owner]
by using splat operator (*
). It would be best to have a relation that will return all members with one query though.– Marcin Kołodziej
Nov 7 at 15:38
hey, that worked! I think that might be the smallest change ever required on Stack Overflow
– Paul Harker
Nov 7 at 15:44
1
It still does 2 selects, which I'd aim to optimize. If you add your relations between
organisation_users
andmembers
to the question, you should be able to get an acceptable answer.– Marcin Kołodziej
Nov 7 at 15:49
concat (<<) permanently alters an object try using + instead
– engineerDave
Nov 7 at 22:04