I have a method as shown:
def all_pages_and_its_n_level_child
@parent_pages = Page.where(:parent_id => params[:page_id])
#this will give me all child of this page
for page in @parent_pages
child_exists=Page.where(:parent_id=>page.id)
#this will give all children of that page
#*I want to make this loop further so that i can check to N-levels,
#this is only for two levels*
end
end
On modern RDBs, recursive CTE can be used to handle recursive structure.
class Page < ApplicationRecord; end
Page.find_by_sql(
"WITH RECURSIVE r AS (
#{Page.where(id: 2).to_sql}
UNION ALL
#{Page.joins('JOIN r').where('r.id = pages.parent_id').to_sql})
SELECT * FROM r")
# Page Load (0.7ms) WITH RECURSIVE r AS (
# SELECT `pages`.* FROM `pages` WHERE `pages`.`id` = 2
# UNION ALL
# SELECT `pages`.* FROM `pages` JOIN r WHERE (r.id = pages.parent_id))
# SELECT * FROM r
# => [#<Page id: 2, parent_id: 1, created_at: "2018-08-21 15:00:43", updated_at: "2018-08-21 15:00:43">, #<Page id: 3, parent_id: 2, created_at: "2018-08-21 15:00:50", updated_at: "2018-08-21 15:00:50">]
As far as I know, mysql, postgres, sqlite3 supports recursive CTE.
EDIT@2020/12/17
On postgresql, you need to have following:
Page.find_by_sql(
"WITH RECURSIVE r AS (
#{Page.where(id: 2).to_sql}
UNION ALL
#{Page.joins('JOIN r ON r.id = pages.parent_id').to_sql})
SELECT * FROM r")
(Thanks to @Dan for pointing out)
Here is some sample (it is not tested! But it will give you some clue):
def all_children(children_array = [])
children = Page.where(parent_id: self.id)
children_array += children.all
children.each do |child|
child.all_children(children_array)
end
children_array
end
So this is recursion that will try to find all children (nested too) from parent. I know that it is very ugly and unefficient, but I hope ii will give you a clue about finding nested elements.
Here is a proper example. I googled this cause I was too lazy to think about it, but since I found this here I decided to do it properly. Here is a code that you can use with the default way you make Rails associations.
def all_children(children_array = [])
children_array += self.children
children.each do |child|
return child.all_children(children_array)
end
return children_array
end
Please note the two uses of there return. If you miss the inside one you will end up with just one level deep tree.
Cheers!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With