Skip to content

Instantly share code, notes, and snippets.

@emmanuel
Forked from FND/.gitignore
Created October 18, 2011 23:50
Show Gist options
  • Save emmanuel/1297105 to your computer and use it in GitHub Desktop.
Save emmanuel/1297105 to your computer and use it in GitHub Desktop.
test case for eager loading of nested associations with DataMapper
Gemfile.lock
#!/usr/bin/env ruby
# encoding: UTF-8
# test case for eager loading of nested associations with DataMapper
require 'rubygems'
require 'dm-core'
require 'dm-constraints'
require 'dm-migrations'
require File.expand_path('../eager_loading', __FILE__)
DataMapper::Logger.new($stdout, :debug)
DataMapper.setup(:default, "sqlite3://#{Dir.pwd}/db.sqlite")
class Person
include DataMapper::Resource
property :id, Serial
property :name, String, :required => true
has n, :vehicles
end
class Vehicle
include DataMapper::Resource
property :id, Serial
property :name, String, :required => true
has n, :components
end
class Component
include DataMapper::Resource
property :id, Serial
property :name, String, :required => true
belongs_to :manufacturer
end
class Manufacturer
include DataMapper::Resource
property :id, Serial
property :name, String, :required => true
end
DataMapper.auto_migrate!
# generate test data
Person.create(:name => "FND", :vehicles => [
Vehicle.create(:name => "Taurus", :components => [
Component.create(:name => "engine",
:manufacturer => Manufacturer.create(:name => "Ford")),
Component.create(:name => "radio",
:manufacturer => Manufacturer.create(:name => "Bose"))
]),
Vehicle.create(:name => "fixie", :components => [
Component.create(:name => "frame",
:manufacturer => Manufacturer.create(:name => "Campagnolo")),
Component.create(:name => "breaks",
:manufacturer => Manufacturer.create(:name => "Shimano"))
])
])
Person.create(:name => "tillsc", :vehicles => [
Vehicle.create(:name => "Golf", :components => [
Component.create(:name => "engine",
:manufacturer => Manufacturer.create(:name => "VW"))
])
])
# retrieve data
puts "", "[INFO] test case A"
person = Person.get!(1)
puts person.vehicles.components.manufacturer.map(&:name).join(", ")
puts "", "[INFO] test case B"
people = Person.all
people.each do |person|
person.vehicles.each do |vehicle|
puts sprintf("%-10s %-10s", person.name, vehicle.name)
end
end
puts "", "[INFO] test case C ===== /!\ n+1 hazard ===="
people = Person.all
people.each do |person|
person.vehicles.each do |vehicle|
vehicle.components.each do |component|
puts sprintf("%-10s %-10s %-10s", person.name, vehicle.name, component.name)
end
end
end
puts "", "[INFO] test case D"
people = Person.all
people.eager_load(Person.vehicles.components).each do |person|
person.vehicles.each do |vehicle|
vehicle.components.each do |component|
puts sprintf("%-10s %-10s %-10s", person.name, vehicle.name, component.name)
end
end
end
# manual eager loading for DataMapper
module EagerLoading
def eager_load(query_path)
query_path.relationships.inject(self) do |collection, relationship|
relationship.eager_load(collection)
end
self
end
end
DataMapper::Collection.send(:include, EagerLoading)
source :rubygems
DM_VERSION = '~> 1.2.0.rc2'
gem 'dm-core', DM_VERSION
gem 'dm-constraints', DM_VERSION
gem 'dm-migrations', DM_VERSION
gem 'dm-sqlite-adapter', DM_VERSION
#!/usr/bin/env sh
rm db.sqlite
# reformat SQL queries for readability
bundle exec ./dm_el.rb | perl -pe 's#SELECT .*? (FROM ".*?" )(.*)#\1SELECT ... \2#'
@emmanuel
Copy link
Author

Output (does this look right?):

[INFO] test case A
 ~ (0.000042) FROM "people" SELECT ... WHERE "id" = 1 LIMIT 1
 ~ (0.000107) FROM "manufacturers" SELECT ... WHERE "id" IN (SELECT "manufacturer_id" FROM "components" WHERE "vehicle_id" IN (SELECT "id" FROM "vehicles" WHERE "person_id" = 1)) ORDER BY "id"
Ford, Bose, Campagnolo, Shimano

[INFO] test case B
 ~ (0.000027) FROM "people" SELECT ... ORDER BY "id"
 ~ (0.000050) FROM "vehicles" SELECT ... WHERE "person_id" IN (1, 2) ORDER BY "id"
FND        Taurus    
FND        fixie     
tillsc     Golf      

[INFO] test case C    ===== /! n+1 hazard ====
 ~ (0.000034) FROM "people" SELECT ... ORDER BY "id"
 ~ (0.000045) FROM "vehicles" SELECT ... WHERE "person_id" IN (1, 2) ORDER BY "id"
 ~ (0.000050) FROM "components" SELECT ... WHERE "vehicle_id" IN (1, 2) ORDER BY "id"
FND        Taurus     engine    
FND        Taurus     radio     
FND        fixie      frame     
FND        fixie      breaks    
 ~ (0.000062) FROM "components" SELECT ... WHERE "vehicle_id" = 3 ORDER BY "id"
tillsc     Golf       engine    

[INFO] test case D
 ~ (0.000035) FROM "people" SELECT ... ORDER BY "id"
 ~ (0.000056) FROM "vehicles" SELECT ... WHERE "person_id" IN (1, 2) ORDER BY "id"
 ~ (0.000058) FROM "components" SELECT ... WHERE "vehicle_id" IN (1, 2) ORDER BY "id"
FND        Taurus     engine    
FND        Taurus     radio     
FND        fixie      frame     
FND        fixie      breaks    
 ~ (0.000068) FROM "components" SELECT ... WHERE "vehicle_id" = 3 ORDER BY "id"
tillsc     Golf       engine    

@d11wtq
Copy link

d11wtq commented Oct 19, 2011

This looks like a promising start in the right direction. For some reason it looks like it's not loading components. It should have FROM "components" SELECT ... WHERE "vehicle_id" IN (1, 2, 3) ORDER BY "id" and not have that final select ;) I'm not too sure why it comes out that way, but DM out of the box produces that output, which struck me as a little odd.

Apologies for not chiming into the other gist last night. I was busy working on some work-related stuff :) Will have another play with this later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment