DataMapper での 1+N 問題
DataMapper では、いわゆる 1+N 問題は起こらないと言われている。
たとえば次のような例。ActiveRecord なら 1+N コの SELECT 文が発行されるけど、DataMapper では 1+1 コの SELECT 文だけを発行する。
$ merb -i irb> IRB.conf[:MAIN_CONTEXT].echo = false # エコーバックを切る irb> employees = Employee.all irb> employees.each {|emp| p emp.department } # 1+1 コの SQL 文 ~ SELECT `id`, `name`, `department_id` FROM `employees` ORDER BY `id` ~ SELECT `id`, `name` FROM `departments` WHERE (`id` IN (3, 1, 2)) ORDER BY `id #<Department id=1 name="Sales"> #<Department id=1 name="Sales"> #<Department id=2 name="Marketing"> #<Department id=3 name="Development">
これは belongs_to の場合だけど、has n の場合も同じように 1+N を避けて 1+1 に動作する。
$ merb -i irb> IRB.conf[:MAIN_CONTEXT].echo = false # エコーバックを切る irb> departments = Department.all irb> departments.each {|dept| p dept.employees } # 1+1 コの SQL 文 ~ SELECT `id`, `name` FROM `departments` ORDER BY `id` ~ SELECT `id`, `name`, `department_id` FROM `employees` WHERE (`department_id` IN (1, 3, 2)) ORDER BY `id` [#<Employee id=1 name="Kathy" department_id=1>, #<Employee id=2 name="Mike" department_id=1>] [#<Employee id=3 name="John" department_id=2>] [#<Employee id=4 name="Bill" department_id=3>]
ただ、many to many にはうまくいかなくて、1+N コの SQL が発行されてしまう。
irb> authors = Author.all irb> authors.each {|author| p author.books } >> authors.each {|author| p author.books } ~ SELECT `id`, `name` FROM `authors` ORDER BY `id` # 1+N コの SQL 文 ~ SELECT `books`.`id`, `books`.`title` FROM `books` INNER JOIN `writings` ON (`books`.`id` = `writings`.`book_id`) WHERE (`writings`.`author_id` = 1) GROUP BY `books`.`id`, `books`.`title` ORDER BY `books`.`id` [#<Book id=1 title="Merb in Action">] ~ SELECT `books`.`id`, `books`.`title` FROM `books` INNER JOIN `writings` ON (`books`.`id` = `writings`.`book_id`) WHERE (`writings`.`author_id` = 2) GROUP BY `books`.`id`, `books`.`title` ORDER BY `books`.`id` [#<Book id=1 title="Merb in Action">] ~ SELECT `books`.`id`, `books`.`title` FROM `books` INNER JOIN `writings` ON (`books`.`id` = `writings`.`book_id`) WHERE (`writings`.`author_id` = 3) GROUP BY `books`.`id`, `books`.`title` ORDER BY `books`.`id` [#<Book id=1 title="Merb in Action">] ~ SELECT `books`.`id`, `books`.`title` FROM `books` INNER JOIN `writings` ON (`books`.`id` = `writings`.`book_id`) WHERE (`writings`.`author_id` = 4) GROUP BY `books`.`id`, `books`.`title` ORDER BY `books`.`id` [#<Book id=2 title="Beginning Merb">] ~ SELECT `books`.`id`, `books`.`title` FROM `books` INNER JOIN `writings` ON (`books`.`id` = `writings`.`book_id`) WHERE (`writings`.`author_id` = 5) GROUP BY `books`.`id`, `books`.`title` ORDER BY `books`.`id` [#<Book id=2 title="Beginning Merb">]
現在の DataMapper では、これが限界。一応ここの改善も ToDo リストには入っているらしんだけど、対応されるのはずいぶん先になりそう。
問題は、これの回避策がないってことなんだよな。Author#books= とか Book#authors= とかが使えればいいんだけど、少なくとも DataMapper 0.9.8 では使えない。
irb> book1.authors = [author1, author2] DataMapper::Associations::ImmutableAssociationError: You can not modify this association from /usr/local/lib/ruby/gems/1.8/gems/dm-core-0.9.7/lib/dm-core/associations/one_to_many.rb:255:in `assert_mutable' from /usr/local/lib/ruby/gems/1.8/gems/dm-core-0.9.7/lib/dm-core/associations/one_to_many.rb:117:in `replace' from /usr/local/lib/ruby/gems/1.8/gems/dm-core-0.9.7/lib/dm-core/associations/one_to_many.rb:21:in `authors=' from (irb):32 from :0
Many to many での、いい解決方法があれば教えてください。