Jess
Jess

Reputation: 3151

How to combine ActiveRecord objects?

I'm wondering if there's an efficient way to combine the results of multiple ActiveRecord objects in Rails. For example, I might make three individual calls to three individual tables, and I want the results combined, and sorted by a common column.

Here's a super basic code example that will hopefully make my question easier to understand:

@results1 = Table1.find(:all)  
@results2 = Table2.find(:all)  
@results3 = Table3.find(:all)  

@combined_results_sorted_by_date_column = (how?)

As suggested by others, here's one solution to the problem.

@combined_results = @result1 + @result2 + @result3
@combined_results.sort! {|x,y| x.date <=> y.date}

What if I want to sort by date, but Table3 refers to the "created_on" column as date?

Upvotes: 8

Views: 8076

Answers (7)

Ijonas
Ijonas

Reputation: 151

I'm not sure what your data volumes are like, but when it comes to sorting, your database will do a better job of it than you ever will using "application layer" code.

Do you need the data returned as an array of model objects seeing as the three tables will probably generate a mixed up array of three distinct model classes ?

Why not use direct-SQL returning rows and columns and have the DB do all the hard work sorting the data ?

Upvotes: 0

Michael Sofaer
Michael Sofaer

Reputation: 2947

I'm assuming you want a mixed array of three different types of ActiveRecord objects, sorted by a date of some kind.

@array = (Bucket.all + Mop.all + Detergent.all).sort{|x,y| x.sort_by <==> y.sort_by}

Since your sort_by field is different for each object type, you need to define it.

class Bucket < ActiverRecord::Base
  def sort_by
    cleaned_on
  end
end

class Detergent < ActiverRecord::Base
  def sort_by
    purchased_on
  end
end

You could pull in all the data sorted in a single query using UNION, but you wouldn't get AR objects out of that.

Upvotes: 0

FlemishBeeCycle
FlemishBeeCycle

Reputation: 557

You're probably not going to like this answer, but I would say you might want to revise your database schema. I was in a similar situation, and sorting the results after concatenating them is definitely not the way you want to go.

Upvotes: 1

nasmorn
nasmorn

Reputation: 2150

@combined_results = @result1 + @result2 + @result3
@combined_results.sort! {|x,y| x.date <=> y.date}

While this surely is not be the most efficient code in the world, it might just be what you need.

If some models don't have a date method I suggest you create one. It is as easy as.

def date
  created_on
end

Upvotes: 0

Lars Haugseth
Lars Haugseth

Reputation: 14881

@results1 = Table1.find(:all)
@results2 = Table2.find(:all)
@results3 = Table3.find(:all)

@combined_results_sorted_by_date_column =
   (@results1 + @results2 + @results3).sort_by(&:date)

What if I want to sort by date, but Table3 refers to the "created_on" column as date?

class Table3
  alias_method :date, :created_on
end

or simply

class Table3
  alias date created_on
end

Upvotes: 14

Carlos Lima
Carlos Lima

Reputation: 4182

You don't work with "Tables" but rather objects.

If you think about it this way, it would make no sense to have:

@results1 = Users.find(:all)
@results2 = Posts.find(:all)
@results3 = Comments.find(:all)

What would the "combined" form of it means?

What you probably want is to combine results from the same kind using different "queries". Is that it?

Upvotes: 2

Ethan
Ethan

Reputation: 60089

@results1 = Table1.find(:all)
@results2 = Table2.find(:all)
@results3 = Table3.find(:all) 

combined = (@results1 + @results2 + @results3).sort { |x, y| x.date <=> y.date }

Upvotes: 0

Related Questions