Bruno Castro
Bruno Castro

Reputation: 27

How to merge ActiveRecord_Associations with Array in Ruby

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

Answers (2)

engineersmnky
engineersmnky

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

SteveTurczyn
SteveTurczyn

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

Related Questions