Skip to content

Commit

Permalink
Skip folded deeper levels when rendering page tree
Browse files Browse the repository at this point in the history
Previously the page tree was rendered in full even if pages are folded.
Consider the following tree: even if level 1 was folded, level 2 and 3
would get rendered and sent to the client.

- root
 |+ level 1
   |- level 2
     |- level 3

This change does two things:

1. load all user-folded pages for the current user in advance, reducing
the number of queries to 1
2. skips all pages that are under a folded page

The result is:

- root
 |+ level 1

Together with 5a4c0d3 this speeds up the page tree rendering from 20
seconds to ~100ms when only 10 folded pages are shown
(Alchemy::Page.count == 4000).
  • Loading branch information
pascalj committed Nov 19, 2017
1 parent 0ed9f0f commit ac564d7
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 9 deletions.
5 changes: 5 additions & 0 deletions app/models/alchemy/folded_page.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,10 @@ class FoldedPage < ActiveRecord::Base
belongs_to :page, required: true
belongs_to :user, required: true,
class_name: Alchemy.user_class_name

def self.folded_for_user(user)
return none unless Alchemy.user_class < ActiveRecord::Base
where(user: user, folded: true)
end
end
end
19 changes: 10 additions & 9 deletions app/serializers/alchemy/page_tree_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,24 @@ def pages
tree = []
path = [{id: object.parent_id, children: tree}]
page_list = object.self_and_descendants
skip_branch = false
base_level = object.level - 1
# Load folded pages in advance
folded_user_pages = FoldedPage.folded_for_user(opts[:user]).pluck(:page_id)
folded_depth = Float::INFINITY

page_list.each_with_index do |page, i|
has_children = page_list[i + 1] && page_list[i + 1].parent_id == page.id
folded = has_children && page.folded?(opts[:user])
folded = has_children && folded_user_pages.include?(page.id)

if skip_branch
next if page.parent_id == path.last[:children].last[:id]

skip_branch = false
if page.depth > folded_depth
next
else
folded_depth = Float::INFINITY
end

# Do not walk my children if I'm folded and you don't need to have the
# full tree.
# If this page is folded, skip all pages that are on a higher level (further down the tree).
if folded && !opts[:full]
skip_branch = true
folded_depth = page.depth
end

if page.parent_id != path.last[:id]
Expand Down
38 changes: 38 additions & 0 deletions spec/models/alchemy/folded_page_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
require "spec_helper"

module Alchemy
class NonArDummyUser; end

describe FoldedPage do
describe "folded_for_user" do
subject(:folded_for_user) { described_class.folded_for_user(user) }
let(:user) { create(:alchemy_dummy_user) }

context "with a non-AR user_class" do
around :each do |example|
before = Alchemy.user_class_name
Alchemy.user_class_name = "NonArDummyUser"
example.run
Alchemy.user_class_name = before
end
let(:user) { NonArDummyUser.new }

it "does not raise an error" do
expect {
folded_for_user
}.not_to raise_error
end
end

context "with folded pages" do
let(:page) { create(:alchemy_page) }
let(:other_user) { create(:alchemy_dummy_user) }
let!(:user_folded) { FoldedPage.create(user: user, page: page, folded: true) }
let!(:other_user_folded) { FoldedPage.create(user: other_user, page: page, folded: true) }

it { is_expected.to include(user_folded) }
it { is_expected.to_not include(other_user_folded) }
end
end
end
end

0 comments on commit ac564d7

Please sign in to comment.