require 'rubygems' require 'sequel' require 'pp' class Sequel::Database def object_graph(master, *relations) from(master).eager_load(*relations) end end class Sequel::Dataset def eager_load(*relations) opts = relations.extract_options! master = @opts[:from].first # alias all columns tables = [master] + relations columns = {} aliases, selects, plans, pkeys, t_counter = {}, [], [], {}, 1 tables.each do |t| c_counter = 0 plan = {} columns[t] = {} @db[t].columns.each do |c| a = :"t#{t_counter}_#{c_counter += 1}" if c == :id pkeys[t] = a end aliases[a] = c columns[t][a] = c plan[c] = a selects << :"#{t}__#{c}".as(a) end plans << plan t_counter += 1 end ds = select(*selects) master_key = :"#{master}__#{opts.values.first}" foreign_key = opts.keys.first relations.each do |t| ds.left_outer_join!(t, foreign_key => master_key) end ds.prepare_graph_method(tables, columns, pkeys) ds end def prepare_graph_method(tables, columns, pkeys) @graph_tables = tables @graph_columns = columns @graph_pkeys = pkeys end def graph() items = Hash.new {|h, k| h[k] = {}} # iterate over records each do |r| @graph_tables.each do |t| id = r[@graph_pkeys[t]] unless items[t][id] rec = @graph_columns[t].inject({}) {|m, kv| m[kv[1]] = r[kv[0]]; m} items[t][id] = rec end end end master_items = items[@graph_tables.first] # resolve references items.each do |t, records| unless t == @graph_tables.first records.each do |k, r| if item = master_items[r[:node_id]] item[t] ||= [] item[t] << r end end end end master_items end end def bb t1 = Time.now yield Time.now - t1 end DB = Sequel("postgres://postgres:postgres@localhost:5432/reality_development") ds = DB.object_graph(:nodes, :attributes, :node_id => :id) # pp ds.graph puts bb {ds.graph}