Reputation: 27
I'm trying to merge a collection of Objects of type ActiveRecord_Associations with others objects in an array.
Here is the piece of code that I'm working :
#Get all projects with the same tag of current project
related_projects = current_team.projects.select{
|project| project.tag_group_id == current_project.tag_group_id && project.id != current_project.id }
#Get all relats of type release in the related projects
related_release_relats = related_projects.flat_map{ |project| project.relats
.find_all { |relat| relat.relat_type == 'release' } }
Is it possible to do something like that ?
I'm trying to merge the current project relats with the related_release_relats( this is all relats from the others projects related )
relats = current_project.relats
relats.merge(related_release_relats)
Thanks everyone
Upvotes: 0
Views: 435
Reputation: 29598
Since I am not 100% clear on your database structure other than what I can glean from your post, I have made some assumptions here.
Given this structure
class Team < ApplicationRecord
has_many :projects
end
class Project < ApplicationRecord
belongs_to :team
belongs_to :tag_group
has_many :relats
end
class TagGroup < ApplicationRecord
has_many :projects
end
class Relats < ApplicationRecord
belongs_to :project
end
We can build a few scopes
(for convenience and re-usability) to put all this work on the database side of things
class Project < ApplicationRecord
belongs_to :team
belongs_to :tag_group
has_many :relats
# find projects related to a given project
scope :related_to,->(project) do
# uses Rails 5 .not
# alternative Rails < 5
# where("tag_group_id =? and id != ?",project.tag_group_id, project.id)
where(tag_group_id: project.tag_group_id).where.not(id: project.id)
end
end
end
class Relats < ApplicationRecord
belongs_to :project
# only Relats that are released
scope :released,-> {where(relat_type: 'released')}
end
Then we will build a small service Object to consolidate this logic
class RelatedRelatFinder
def initialize(current_project,current_team)
@current_project = current_project
@current_team = current_team
end
# method to retrieve the current_project#relats and
# all the current_teams released relats related to the current_project
def find_all_relats
# uses Rails 5 .or
# alternative Rails < 5
# where("relats.id IN(?) OR relats.id IN(?)",
# @current_project.relats.to_sql,
# released_related_relats.to_sql)
Relat.where(id: @current_project.relats)
.or(Relat.where(id: released_related_relats))
end
private
# This is the method for getting the current_teams released
# relats related to the current_project
def released_related_relats
Relat.released.merge(
@current_team.projects.merge(
Project.related_to(@current_project)
)
)
end
end
Then usage is as simple as:
relat_finder = RelatedRelatFinder.new(current_project,current_team)
relat_finder.find_all_relats
and this will generate a SQL query similar to:
SELECT relats.*
FROM relats
WHERE
(relats.id IN (
SELECT relats.id
FROM relats
WHERE
relats.project_id = 1 #current_project.id
) OR
relats.id IN (
SELECT relats.id
FROM relats
INNER JOIN projects ON projects.id = relats.project_id
WHERE
relats.relat_type = 'released'
AND projects.team_id = 1 #current_team.id
AND projects.tag_group_id = 1 #current_project.tag_group_id
AND (projects.id != 1) # not current_project.id
)
)
Which seems to be what you are asking for. Now the code is more understandable, more organized, there are no intermediate Array
being created and the Database is providing all the heavy lifting.
Upvotes: 0
Reputation: 36880
There's no merge
method for arrays but you can add arrays together.
current_project.relats + release_relats
If you want to ensure you don't include the same relats multiple times use uniq
...
(current_project.relats + release_relats).uniq
If you want an ActiveRecord_Relation
you can do...
Relat.where(id: (current_project.relats + release_relats).map(&:id))
Upvotes: 1