Я очень новичок в рельсах, и я немного зациклился на логике этой проблемы.
У меня есть одна таблица (с использованием mysql) сотрудников, каждая из которых manager_id
ключ manager_id
который относится к сотруднику, которому они сообщают. Так, например, сотрудник с названием "CEO" с идентификатором 1, имеет manager_id из nil, а сотрудник с названием "CTO" имеет manager_id из 1. Таким образом, мои записи выглядят так:
id: 1, first_name: "Bob", last_name: "Boss", title: "CEO", manager_id: null
id: 2, first_name: "Pat", last_name: "Guy", title: "CTO", manager_id: 1
id: 3, first_name: "John", last_name: "Dude", title: "VP of engineering", manager_id: 2
и моя структура JSON должна выглядеть так
[
{id: 1, first_name: "Bob", last_name: "Boss", title: "CEO", manager_id: null, descendents: [
{id: 2, first_name: "Pat", last_name: "Guy", title: "CTO", manager_id: 1, descendents: [
{id: 3, first_name: "John", last_name: "Dude", title: "VP of engineering", manager_id: 2, descendents: [....]}
]},
{..more CEO descendents...}
]
Я пытаюсь создать вложенную структуру JSON, которая начинается с CEO, перечисляет всех сотрудников, которые отчитываются перед ними, и каждого из этих потомков сотрудников. Я пытался написать сценарий, который создает это, но я продолжаю получать бесконечные рекурсивные вызовы. Это то, что у меня есть
#start at some root
@root = Employee.find_by title: 'CEO'
#convert to hash table
@results[0] = @root.attributes
#add direct_reports key
@results[0]["direct_reports"] = []
def getBelow(root=@root)
@reports = Employee.where("manager_id = ?", @root[:id])
if @reports.blank?
return []
else
@reports = @reports.map(&:attributes)
@reports.each do |person|
person["direct_reports"] = []
getBelow(person)
end
@reports = Employee.where("manager_id = ?", @root[:id])
root["direct_reports"] = @reports
end
return @root
end
@list = getBelow(@results[0])
Если я передаю каждому объекту нового человека, не должны ли они в конечном итоге закончиться, когда @reports.blank?
становится правдой?
Альтернатива, о которой я думал, заключалась в использовании ассоциаций таблиц, вдохновленных этим сообщением в блоге
https://hashrocket.com/blog/posts/recursive-sql-in-activerecord
но это кажется слишком сложным.
Некоторые проблемы в методе getBelow
Вы всегда используете @root вместо использования param (root). Поэтому вы всегда начинаете снова с "CEO".
Вы вызываете getBelow рекурсивно, но вы не используете результат.
Вы вызываете @reports = Employee.where("manager_id =?", @root[:id])
дважды.
Вы возвращаетесь @root.
Как сказал Хорхе Наджера, есть драгоценные камни, которые легко справляются с древовидной структурой. Если вы хотите построить его самостоятельно, это мое предложение:
#start at some root
@root = Employee.find_by manager_id: nil
#convert to hash table
@tree = @root.attributes
#add direct_reports key
@tree["direct_reports"] = getBelow(@root)
def getBelow(manager)
branch = []
Employee.where("manager_id = ?", manager.id).each do |employee|
node = employee.attributes
node["direct_reports"] = getBelow(employee)
branch << node
end
branch
end
Это не было проверено, поэтому я думаю, что вы получите некоторые ошибки, но я считаю, что идея в порядке.
branch << node
добавляет элемент (узел) в массив (ветвь). ветвь начинается как пустой массив и добавляет элементы внутри каждого цикла. Поскольку цикл вызывает getBelow (рекурсивно), сам узел может иметь много уровней (подветвлений). После end
ветвь имеет все узлы, добавленные в цикл (и каждый узел может иметь подузлы, добавленные в рекурсивных вызовах). Вы можете передать любой manager_id для запуска и заменить элемент @root.