Как сделать условную загрузку в ActiveRecord?
У меня есть тег модели, который является моделью дерева (используя closure_tree
) и благодаря жемчужине я могу делать такие вещи, как:
Tag.includes(:children).first
чтобы загружать своих детей. Теперь у меня есть метод экземпляра, который также использует детей. Поэтому, когда я получаю тег по указанному запросу, перебираем тег и его дочерние элементы и вызываем этот метод экземпляра, bullet
Джем жалуется, что я делаю N+1 запросов и советует мне include(children)
, Это происходит потому, что я не загрузил дочерние элементы дочерних элементов, поэтому при вызове метода экземпляра я делаю отдельный запрос для дочерних элементов каждого вложенного тега тега первого уровня.
Я могу решить это, сделав следующее:
Tag.includes(children: :children).first
В этом случае ActiveRecord активно загружает тег, его дочерние элементы и дочерние элементы его дочерних элементов. Это, однако, работает, если есть только 3 поколения - тег - это дедушка и бабушка, а затем приходят родители (которые являются дочерними элементами корневого тега) и внуки (которые являются дочерними элементами корневого тега). Если у меня, скажем, 4 поколения, то я должен сделать:
Tag.includes(children: {children: :children}).first
Так что, если я могу определить depth
самого дальнего внука корневого тега, есть ли способ условно построить мой запрос - если глубина равна 2, используйте includes(:children)
, если глубина 3 - используйте includes(children: :children)
и так далее?
2 ответа
Судя по документации по адресу: https://github.com/mceachen/closure_tree вы должны использовать
Tag.descendants
Tag.self_and_descendants
Это извлечет все дочерние элементы, дочерние элементы и т. Д., Пока не закончится на листе.
Лучшее решение, которое я мог придумать, это:
def included_children(tag_id)
depth = Tag.find(tag_id).leaves.last.depth
eval(includes_string(depth))
end
def includes_string(depth)
"{children: #{depth > 1 ? includes_string(depth - 1) : ':children'}}"
end
Я сначала нахожу depth
из самого глубокого отпуска. Это говорит мне, сколько раз я должен позвонить :children
, Затем я строю строку на основе глубины и оцениваю строку в коде, которую я использую в includes
метод. Любые предложения по улучшению приветствуются, потому что у этого решения есть несколько недостатков, и я считаю его совершенно нечистым. Спасибо.