Rails3: Избегать выбора n + 1 с Gem Ancestry? - PullRequest
3 голосов
/ 05 августа 2011

Требования:

Я создаю приложение со списком задач и хотел, чтобы у задач были подзадачи.
Я также хотел, чтобы задачи могли существовать в нескольких местах дерева, например, если бы у меня было 2 задачи:

  1. Сборка питомника собак
  2. Поставить новый забор

Если бы я планировал построить собачью будку из того же материала, что и забор, обе эти задачи имели бы подзадачу «Купить ограждения».

Моя проблемная реализация (обратная связь приветствуется):

У меня есть 2 модели:

  • Узел (has_ancestry и assign_to: task)
  • Задача (has_many: node)

Это означает, что дерево (которое позволяет мне иметь подзадачи) не хранит задачу само по себе, просто ссылку на объект задачи.

Вот пример использования консоли rails:

t1 = Task.create :name => "Build dog kennel"
n1 = Node.create :task => t1

t2 = Task.create :name => "Put up new fence"
n2 = Node.create :task => t2

t3 = Task.create :name => "Buy fence palings"
n11 = Node.create :task => t3, :parent => n1
n21 = Node.create :task => t3, :parent => n2

t4 = Task.create :name => "Construct the fence"
n22 = Node.create :task => t4, :parent => n2

n2.children.each { |c| puts c.task.name }

Эта последняя строка дает следующий вывод, указывая выбор n + 1:

Node Load (0.2ms)  SELECT "nodes".* FROM "nodes" WHERE "nodes"."ancestry" = '12'
Task Load (0.2ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."id" = 11 LIMIT 1
Buy fence palings
Task Load (0.2ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."id" = 10 LIMIT 1
Put up new fence

Помощь

Я довольно новичок в Ruby on Rails и ActiveRecord, но я думаю, что все, что мне нужно сделать, это соединить таблицу узлов с таблицей задач на основе внешнего ключа node.task_id, но я просмотрел Документация по родословной и не могу найти ничего полезного.

В будущем я планирую получать дополнительную информацию из объекта задачи также с помощью внешних ключей, таких как автор, связанные комментарии и т. Д., И с этой реализацией загрузка одной страницы может вызвать довольно много запросов выбора: (*

Может кто-нибудь предложить мне, как это сделать?
Есть ли способ принудительной загрузки? (Это поможет?)
Я открыт для обратной связи, если у вас есть лучшая идея, как это сделать.

Заранее спасибо!

1 Ответ

7 голосов
/ 31 октября 2011

Итак, поиграв некоторое время, я наконец нашел способ сделать это.

Вместо этой строки:

n2.children.each { |c| puts c.task.name }

, что приводит к следующему:

Node Load (0.2ms)  SELECT "nodes".* FROM "nodes" WHERE "nodes"."ancestry" = '27'
Task Load (0.2ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."id" = 23 LIMIT 1
Buy fence palings
Task Load (0.2ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."id" = 24 LIMIT 1
Construct the fence

Я использовал эту строку:

n2.children.find(:all, :include => :task).each { |c| puts c.task.name }

Что привело к этому:

Node Load (0.2ms)  SELECT "nodes".* FROM "nodes" WHERE "nodes"."ancestry" = '27'
Task Load (0.2ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."id" IN (23, 24)
Buy fence palings
Construct the fence

Это должно выполнить только 2 запроса, независимо от размера, и результирующий набор будет включатьзадачи!
Я знаю, что это, вероятно, базовые вещи, но для новичков, таких как я, это может немного сбивать с толку, поскольку в разделе направляющих рельсов, который ссылается на готовую загрузку , показан только метод класса* 1018 1019 * **

...