<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="354786" data-post-id="354786">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="garrison" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  garrison
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="MarthinL" data-post="20" data-topic="69023">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/m/3da27b/48.png" class="avatar"> MarthinL:</div>
<blockquote>
<p>and I’m not sure I understand exactly what establishes and/or affect that default behaviour</p>
</blockquote>
</aside>
<p>The <code>update/2</code> clause that comment is placed before pattern matches on the <em>current</em> <code>socket</code> to determine whether the <code>entry</code> is already set, and if so it avoids streaming the children. In effect, it ensures that the children of a parent are only ever rendered <em>the first time</em> the tree is rendered (via the second <code>update/2</code> clause, which is only reached when <code>entry</code> is not yet set), and after that they have to be updated individually by a <code>send_update</code>.</p>
<p>In fact, as I read more closely, I don’t believe it’s possible to update the <em>structure</em> of the tree <em>at all</em> in this example - but of course it is only an example.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="354786" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/using-streams-with-recursive-and-or-deeply-nested-schemas/69023/22">Post #21</a>
	                </div>
	            </div>
              <div id="likers-container-354786" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="354786"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #21"></div>
  </section>
</div>
    <div class="postbit" id="354788" data-post-id="354788">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="MarthinL" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  MarthinL
                      <span class="op-star" title="Thread Starter">
                        <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                      </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="garrison" data-post="22" data-topic="69023">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/g/3bc359/48.png" class="avatar"> garrison:</div>
<blockquote>
<p>The <code>update/2</code> clause that comment is placed before pattern matches on the <em>current</em> <code>socket</code> to determine whether the <code>entry</code> is already set, and if so it avoids streaming the children.</p>
</blockquote>
</aside>
<p>Ah, now I notice it. Cool, thanks.</p>
<aside class="quote no-group" data-username="garrison" data-post="22" data-topic="69023">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/g/3bc359/48.png" class="avatar"> garrison:</div>
<blockquote>
<p>In fact, as I read more closely, I don’t believe it’s possible to update the <em>structure</em> of the tree <em>at all</em> in this example - but of course it is only an example.</p>
</blockquote>
</aside>
<p>In <a href="https://forum.elixirforum.com/t/using-streams-with-recursive-and-or-deeply-nested-schemas/69023/21" rel="nofollow">my rant about handling structure separately</a> i conclude not having access to the structure via the LiveComponent for each node is probably a good thing. TL/DR: I’m given to the idea of splitting the problem to separate concerns - one dealing exclusively with structure and the other with the contents of each node.</p>
<p>PS. I did better catching the gist of the gist once I realised I should regard TreeComponent as the NodeComponent because it doesn’t operate on the tree, only on a Node.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="354788" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/using-streams-with-recursive-and-or-deeply-nested-schemas/69023/23">Post #22</a>
	                </div>
	            </div>
              <div id="likers-container-354788" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="354788"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #22"></div>
  </section>
</div>
    <div class="postbit" id="354791" data-post-id="354791">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="garrison" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  garrison
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="MarthinL" data-post="23" data-topic="69023">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/m/3da27b/48.png" class="avatar"> MarthinL:</div>
<blockquote>
<p>I’m given to the idea of splitting the problem to separate concerns - one dealing exclusively with structure and the other with the contents of each node</p>
</blockquote>
</aside>
<p>Note that this is what I described in one of my more recent replies (about the maps), and I found that approach to be helpful when dealing with updates that come in (via PubSub in my case) which touch a given record. The reason I took that path is that, otherwise, I would have to write recursive functions to “patch” the tree and I prefer to just use indirection (especially since I have very complex functionality which lends itself to recomputing the final tree as I explained).</p>
<p>However, what I meant with regards to the example was simply that, because the <code>children</code> list is never re-rendered, the structure of the tree can never change: i.e. you could never re-order a set of nodes, or move a node elsewhere. But it is just an example.</p>
<aside class="quote no-group" data-username="MarthinL" data-post="23" data-topic="69023">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/m/3da27b/48.png" class="avatar"> MarthinL:</div>
<blockquote>
<p>once I realised I should regard TreeComponent as the NodeComponent because it doesn’t operate on the tree, only on a Node</p>
</blockquote>
</aside>
<p>Ah, but if you were a philosopher you would recognize that a Node is nothing more than a smaller Tree <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20"></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="354791" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/using-streams-with-recursive-and-or-deeply-nested-schemas/69023/24">Post #23</a>
	                </div>
	            </div>
              <div id="likers-container-354791" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="354791"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #23"></div>
  </section>
</div>
    <div class="postbit" id="354826" data-post-id="354826">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="MarthinL" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  MarthinL
                      <span class="op-star" title="Thread Starter">
                        <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                      </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="garrison" data-post="24" data-topic="69023">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/g/3bc359/48.png" class="avatar"> garrison:</div>
<blockquote>
<p>Note that this is what I described in one of my more recent replies (about the maps)</p>
</blockquote>
</aside>
<p>I didn’t notice it to be the same, and still don’t to be honest. It’s similar, related or even converging, but you described (the map) as a way to keep the nodes in a flat structure but if I understood correctly each node would still contain its own child list, whether that’s in effect a copy or a list of references by map key, the structure is still within the data. I’m proposing taking that just one step further and moving the knowledge about each node’s children out of the flattened data. Practically this means the association representing the list of children of a node reverted to :not_loaded.</p>
<p>The structure exlusively lives in a different calculated value and passed to the appropriate LiveView component as an assign, and a very lightweight one at that since the structure data fits snugly into a list of either integers or 2-tuples with an integer and recursive list. Once the structure is in that form and completely independent from the data/HTML associated with each id in the tree, any updates to it can be succintly reduced to a series of primitives which becomes the basis for updating the rendered content correctly in any situation from loading a different root which replaces all the content to adding or moving child nodes to updating a parent without touching any children to moving any portion of the tree from one parent to another. Those are all things we know how to do with trees and therefore with simple nested lists with a small enough memory footprint so we can make and compare several interim copies of it if we need to at very low cost. Plus you only need to do it once, ever. None of what you implement in structure manipulation gets affected in any way when there are changes to the content schemas.</p>
<aside class="quote no-group" data-username="garrison" data-post="19" data-topic="69023">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/g/3bc359/48.png" class="avatar"> garrison:</div>
<blockquote>
<p>the reason I mention using a <code>map</code> is because retrieval by primary key is <code>O(1)</code></p>
</blockquote>
</aside>
<p>Really, O(1)? I’d buy O(log n), but for the map code to find the key it has to, one way or another, search through the keys. My understanding is that the keys are kept in a balanced tree of sorts to minimise the number of comparisons it needs to make on average to find a key, but unavoidably as the number of keys grow access time must eventually get slower. I was also lead to believe that maps, sets and mapsets all use the exact same mechanism anyway, so I think your assumption that, being sets, mapsets would unavoidably be slower might be a little off or even false.</p>
<p>The way lists live in memory means all access has to be from the head walking recursively into each tail. That fundamental recursion is reduced to mere iteration through the magic of (originally Erlang’s) tail recursion detection and optimisation. The array-like semantics of lists is purely an illusion. It does come with substantial benefits, not the least of which is that a list may contain not only variable size elements but also elements of different types. Most of the list versions of Enum has been operating at maximum optimisation levels for a long time resulting in really good performance, but it will never be actually possible to access it like an array by calculating an element offset from an index.</p>
<p>Several of the underlying Erlang data structures including sets and gb_trees if I remember correctly, use internally defined and managed memory constructs which are opague to the user. That allows them to forfeit variable length elements and store data in fixed element size arrays with actual array access performance, i.e. calculating the address of an element as a starting point plus an index times the element size. This resulted in some highly efficient techniques from the world of advanced data structures and algorithms becoming available in Erlang and through that also to Elixir where they ended up being put to use to implement maps.</p>
<p>Erlang didn’t have maps (it had records, with metadata only at compile-time, not runtime, which wasn’t great) until Elixir formulated and implemented the concept which ultimately made its way bak to Erlang. The core of Elixir is still written in Erlang and I believe the data structures used to create Map wasn’t custom written exclusively for Elixir’s Map type. Map either used pre-existing Erlang library functions or what was done to make Map run faster was not exclusively for Map but helped improve efficiency for many other structures as well.</p>
<p>Disclaimer: It’s all open source but I’ve never been directly involved in creating or maintaing any of the Erlang, OTP, Elixir, Phoenix, Ecto or LiveView code nor have I made any sort of habit of trying to understand the underlying code. I’ve merely been professionally aware of Erlang from before it was open-sourced, evangelised many of my peers into taking a look at it who built their entire careers and businesses around it since, written a few small systems in Erlang and planned to write my life’s work’s proper server in Erlang until I discovered how far I can get how fast using Elixir and Phoenix instead. Which is a long way of saying I don’t know these things for fact because I lived it, but through keen observation over a long time, albeit usually at a fairly abstract level only. I did recently go read a bit of the Elixir code for clues as to how I might implement this unicorn dual-natured structure I thought I needed. Only after that (and the confirmation that Elixir lists are directly based on Erlang’s lists) did i truly realise that what is called a linked list is really a completely unbalanced tree turned on its side. That’s when all the pieces of the picture fell into place for me and I was able to see that all the “pointers” I thought I’d have to implement and manage in keeping with both Elixir and Erlang’s immutable data principles are already present in the list construct. It is absolutely perfect for the job and I couldn’t have hoped for a more optimal set of tools to manipulate them with already tested to the fullest possible extent.</p>
<p>It is clear as day to me that nested lists of primary key values represents the structural part of a tree so accurately and effeciently that there is not a shred of doubt in my mind that it’s the ideal choice of how to keep the strucure of any portion of the data in memory.</p>
<aside class="quote no-group" data-username="garrison" data-post="24" data-topic="69023">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/g/3bc359/48.png" class="avatar"> garrison:</div>
<blockquote>
<p>Ah, but if you were a philosopher you would recognize that a Node is nothing more than a smaller Tree <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20"></p>
</blockquote>
</aside>
<p>Yeah, or because I am one I differentiate between a Node and a Tree. <img src="https://forum.elixirforum.com/images/emoji/apple/smile.png?v=15" title=":smile:" class="emoji" alt=":smile:" loading="lazy" width="20" height="20"></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="354826" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/using-streams-with-recursive-and-or-deeply-nested-schemas/69023/25">Post #24</a>
	                </div>
	            </div>
              <div id="likers-container-354826" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="354826"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #24"></div>
  </section>
</div>
    <div class="postbit" id="354898" data-post-id="354898">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="garrison" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  garrison
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="MarthinL" data-post="25" data-topic="69023">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/m/3da27b/48.png" class="avatar"> MarthinL:</div>
<blockquote>
<p>Really, O(1)? I’d buy O(log n), but for the map code to find the key it has to, one way or another, search through the keys.</p>
</blockquote>
</aside>
<p>Well no, not exactly. Associative arrays in most programming languages are built on hash tables, which are <code>O(1)</code> <em>if there are no hash collisions</em>. Which is a big if, but people usually call them <code>O(1)</code> even though I think it’s technically <code>Omega(1)</code> for that reason. Note that the trade-off versus a tree here is that hash tables are not order-preserving.</p>
<p>Erlang maps are actually <a href="https://www.erlang.org/doc/system/maps#how-large-maps-are-implemented" rel="nofollow">technically hash array-mapped tries</a> which are a more exotic hybrid data structure. I’m not sure the exact performance characteristics but you can investigate further if you are interested <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20"></p>
<aside class="quote no-group" data-username="MarthinL" data-post="25" data-topic="69023">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/m/3da27b/48.png" class="avatar"> MarthinL:</div>
<blockquote>
<p>I was also lead to believe that maps, sets and mapsets all use the exact same mechanism anyway, so I think your assumption that, being sets, mapsets would unavoidably be slower might be a little off or even false.</p>
</blockquote>
</aside>
<p>But sets have no method to retrieve an object by key <em>at all</em> - you would have to brute-force iterate through the whole thing.</p>
<p>The use-case I was describing was to store the tree in one data structure (like you said) and then <em>join</em> it with the associated records by looking them up in a map. You would not use a set for this.</p>
<p>The fact that sets <em>internally</em> use maps is an implementation detail.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="354898" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/using-streams-with-recursive-and-or-deeply-nested-schemas/69023/26">Post #25</a>
	                </div>
	            </div>
              <div id="likers-container-354898" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="354898"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #25"></div>
  </section>
</div>
    <div class="postbit" id="354901" data-post-id="354901">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="garrison" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  garrison
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="MarthinL" data-post="25" data-topic="69023">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/m/3da27b/48.png" class="avatar"> MarthinL:</div>
<blockquote>
<p>but you described (the map) as a way to keep the nodes in a flat structure but if I understood correctly each node would still contain its own child list</p>
</blockquote>
</aside>
<p>Not quite. Let me try to demonstrate what I did but in the context of the (simpler) files/folders example.</p>
<p>There are three database tables with corresponding Ecto schemas: <code>nodes</code>, <code>files</code>, and <code>folders</code>. The <code>nodes</code> store the structure of the tree and contain foreign keys pointing to <code>files</code> and <code>folders</code>. The <code>files</code> and <code>folders</code> tables then store metadata, like names and so on.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">%Node{id: 1, folder_id: 1, file_id: nil, parent_id: nil} # Root folder node
%Node{id: 2, folder_id: nil, file_id: 1, parent_id: 1} # File node
%Folder{id: 1, name: "elixir stuff"} # Folder metadata
%File{id: 1, name: "elixir.jpg"} # File metadata
</code></pre>
<p>This tree only has two nodes, but you could imagine an arbitrary filesystem structure. Now, you could write a recursive query to load the tree from the database. You then might join every <code>Node</code> with its associated <code>Folder</code> or <code>File</code> to get the whole tree.</p>
<p>The annoying bit, though, is that now if the user updates a folder (a rename, say) then the new version will come in over PubSub. And you have to recursively walk the tree, find that Folder node, and update it. This is valid, but there is another approach: use indirection.</p>
<p>Instead, you could avoid joining the files and folders to the nodes in the query, and instead load them separately. Then you can store them in maps (<code>files = %{file.id =&gt; file}</code> and so on) and join them at runtime by looking up the <code>File</code> for a <code>file_id</code> on a <code>Node</code>.</p>
<p>Now you can simply update the <code>folders</code> map when a new <code>Folder</code> comes in over PubSub, and then it will appear next render. I <em>think</em> this is in the same vein as what you are talking about, but correct me if I’m wrong.</p>
<p>There is one problem with this approach: LiveView is not smart enough to diff the arbitrary <code>folders</code> lookups, so any change to <code>folders</code> would re-render the entire tree and send the whole thing down the wire. I avoid <em>that</em> problem by materializing the tree <em>myself</em> into a simplified representation which I then pass to the LiveComponents. So the “simplified tree” comes out looking like:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">%{
  id: 1, type: :folder, name: "elixir stuff", children: [
    %{id: 2, type: :file, name: "elixir.jpg"},
  ],
}
</code></pre>
<p>This might sound like a lot of extra complexity, but my use-case is <em>very complicated</em> because the UI is highly interactive and has a lot of moving parts. I iterated several times before arriving at this design, which I found to be radically simpler and more performant. Your situation may vary of course.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="354901" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/using-streams-with-recursive-and-or-deeply-nested-schemas/69023/27">Post #26</a>
	                </div>
	            </div>
              <div id="likers-container-354901" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="354901"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #26"></div>
  </section>
</div>
    <div class="postbit" id="354905" data-post-id="354905">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="MarthinL" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  MarthinL
                      <span class="op-star" title="Thread Starter">
                        <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                      </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="garrison" data-post="26" data-topic="69023">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/g/3bc359/48.png" class="avatar"> garrison:</div>
<blockquote>
<p>But sets have no method to retrieve an object by key <em>at all</em> - you would have to brute-force iterate through the whole thing.</p>
<p>The use-case I was describing was to store the tree in one data structure (like you said) and then <em>join</em> it with the associated records by looking them up in a map. You would not use a set for this.</p>
<p>The fact that sets <em>internally</em> use maps is an implementation detail.</p>
</blockquote>
</aside>
<p>I think we’ve misunderstood each other on this. Map for the flattened content is spot on and I was discussing how one can store the structure as nested lists. I believe you made reference to using maps in the context of the streams discussion before I decided to physically split the structure and content. But it’s no issue, we’re in agreement that the bulky node data can live in maps if you’re going to keep them in memory, or loaded from database if you need to save on memory and if you need it, they can go into streams as well. As long as you can “surgically” remote control the DOM, i.e. to change the children shown content of container or not depending on how the structure changes, it should work well.</p>
<p>I haven’t yet figured out if it really will require a LiveComponent per node as per the example or not or what the overheads for that is like, but I’ll get there.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="354905" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/using-streams-with-recursive-and-or-deeply-nested-schemas/69023/28">Post #27</a>
	                </div>
	            </div>
              <div id="likers-container-354905" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="354905"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #27"></div>
  </section>
</div>
    <div class="postbit" id="354919" data-post-id="354919">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="garrison" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  garrison
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="MarthinL" data-post="28" data-topic="69023">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/m/3da27b/48.png" class="avatar"> MarthinL:</div>
<blockquote>
<p>LiveComponent per node as per the example or not or what the overheads for that is like</p>
</blockquote>
</aside>
<p>If you want to minimize diffs over the wire you will probably want one LiveComponent per node as discussed previously. The overhead for LiveComponents is not very large, they are really just a vehicle for maintaining the diffs and that overhead is obviously unavoidable. Importantly LiveComponents live in the same process as the parent LiveView so when you pass things to them they will share that memory (no copying).</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="354919" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/using-streams-with-recursive-and-or-deeply-nested-schemas/69023/29">Post #28</a>
	                </div>
	            </div>
              <div id="likers-container-354919" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="354919"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #28"></div>
  </section>
</div>
    <div class="postbit" id="354920" data-post-id="354920">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="MarthinL" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  MarthinL
                      <span class="op-star" title="Thread Starter">
                        <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                      </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="garrison" data-post="27" data-topic="69023">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/g/3bc359/48.png" class="avatar"> garrison:</div>
<blockquote>
<p>There is one problem with this approach: LiveView is not smart enough to diff the arbitrary <code>folders</code> lookups, so any change to <code>folders</code> would re-render the entire tree and send the whole thing down the wire. I avoid <em>that</em> problem by materializing the tree <em>myself</em> into a simplified representation which I then pass to the LiveComponents. So the “simplified tree” comes out looking like:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">%{
  id: 1, type: :folder, name: "elixir stuff", children: [
    %{id: 2, type: :file, name: "elixir.jpg"},
  ],
}
</code></pre>
<p>This might sound like a lot of extra complexity, but my use-case is <em>very complicated</em> because the UI is highly interactive and has a lot of moving parts. I iterated several times before arriving at this design, which I found to be radically simpler and more performant. Your situation may vary of course.</p>
</blockquote>
</aside>
<p>Yes, that is a LiveView challenge that to date we’ve only seen one viable solution for. I’m hoping more will come to light.</p>
<p>Our approaches are converging. I’ve taken the simplification of the tree view a whole lot further until it genuinely only contains the IDs by which to find the actual content from whereever they are, but it’s the same principle. You’re storing the simplified tree still in nested maps, I’m using essentially nested lists or lists of either integers (meaning it’s a leaf node) or tuples {id, child_id_list} meaning it’s a branch or node with children.</p>
<p>For completeness, I’m actually storing the structure in two parts. Both are list based, but the semantics are slightly different. The first is a straight list of ids representing the path to the node that forms the “root” of the tree data being displayed, like breadcrumbs. All the nodes in that list (must have) children (in order to be part of the path) but I’m not storing any detail about how many other siblings each node miight have or such as that’s not relevant data. The second part is the recursive structure of the part of the data for which HTML has been generated and sent to the client that’s been extracted from the nested associations before they’re flattened (with the recursive association reset to not_loaded).</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="354920" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/using-streams-with-recursive-and-or-deeply-nested-schemas/69023/30">Post #29</a>
	                </div>
	            </div>
              <div id="likers-container-354920" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="354920"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #29"></div>
  </section>
</div>
    <div class="postbit" id="354923" data-post-id="354923">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="MarthinL" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  MarthinL
                      <span class="op-star" title="Thread Starter">
                        <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                      </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="garrison" data-post="29" data-topic="69023">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/g/3bc359/48.png" class="avatar"> garrison:</div>
<blockquote>
<p>really just a vehicle for maintaining the diffs and that overhead is obviously unavoidable</p>
</blockquote>
</aside>
<p>The jury is still out (in my case) about that (both the diffs part and the unavoidability of the overhead). I don’t have sufficient command over all the tools at my disposal just yet. It is obviously in my interest to keep the load per user session to a minimum, but there’s still too many variables and at some point it will have a run-in with the law of diminishing returns.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="354923" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/using-streams-with-recursive-and-or-deeply-nested-schemas/69023/31">Post #30</a>
	                </div>
	            </div>
              <div id="likers-container-354923" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="354923"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #30"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <a class="load-more-button" data-turbo-stream="true" href="/topics/69023/load_more?page=4">Load more posts (44 remaining)</a>
</div></template></turbo-stream>