<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Syd Amir</title>
    <description>The latest articles on DEV Community by Syd Amir (@syd_amir_5353ffa4816e3bc5).</description>
    <link>https://dev.to/syd_amir_5353ffa4816e3bc5</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1645174%2F902c365e-fa79-407d-9107-050d4477c4c3.jpg</url>
      <title>DEV Community: Syd Amir</title>
      <link>https://dev.to/syd_amir_5353ffa4816e3bc5</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/syd_amir_5353ffa4816e3bc5"/>
    <language>en</language>
    <item>
      <title>The Nature of Data: Why Lesan's Relationships Win in the Real World</title>
      <dc:creator>Syd Amir</dc:creator>
      <pubDate>Wed, 19 Aug 2026 11:27:16 +0000</pubDate>
      <link>https://dev.to/syd_amir_5353ffa4816e3bc5/the-nature-of-data-why-lesans-relationships-win-in-the-real-world-291k</link>
      <guid>https://dev.to/syd_amir_5353ffa4816e3bc5/the-nature-of-data-why-lesans-relationships-win-in-the-real-world-291k</guid>
      <description>&lt;p&gt;&lt;em&gt;In the last article we showed the mechanism — Lesan turns a ~2,550,250-document read into a ~25,250-document read by embedding relationships and keeping them in sync. This article answers the obvious objection: "But what about the writes?" The answer comes from the nature of data itself.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;By the Lesan team&lt;/strong&gt; · &lt;a href="https://github.com/MiaadTeam/lesan" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; · &lt;a href="https://miaadteam.github.io/lesan/" rel="noopener noreferrer"&gt;Docs&lt;/a&gt; · &lt;a href="https://dev.to/syd_amir_5353ffa4816e3bc5/why-lesan-we-built-a-framework-that-turns-on2-queries-into-olog-n-1f45"&gt;Read part 1&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The objection
&lt;/h2&gt;

&lt;p&gt;Any engineer who reads &lt;a href="https://medium.com/@hemedani/why-lesan-we-built-a-framework-that-turns-o-n%C2%B2-queries-into-o-log-n-72d031a5103f" rel="noopener noreferrer"&gt;Why Lesan&lt;/a&gt; — where embedded relationships turn a ~2.5-million-document request into a ~25-thousand-document one — asks the same question within seconds:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"OK, but you're duplicating data. Every time something changes, you have to update all those embedded copies. Isn't that a nightmare on writes?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's a fair question. Manual embedding &lt;em&gt;is&lt;/em&gt; a nightmare on writes — we showed the &lt;code&gt;updateMany&lt;/code&gt; + &lt;code&gt;arrayFilters&lt;/code&gt; horror in part 1. The difference is that Lesan automates the sync. But the deeper objection remains: even automated, doesn't maintaining N embedded copies make every write N times more expensive?&lt;/p&gt;

&lt;p&gt;The honest answer is: &lt;strong&gt;sometimes yes — and it doesn't matter.&lt;/strong&gt; Because of the nature of data models, the writes that cost you the most are exactly the ones that happen least often.&lt;/p&gt;

&lt;p&gt;Let's look at real projects to see why.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The law of data
&lt;/h2&gt;

&lt;p&gt;Step back from frameworks for a moment and look at what data actually does in the wild:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A blog article is &lt;strong&gt;read&lt;/strong&gt; thousands of times a day, but &lt;strong&gt;written&lt;/strong&gt; once.&lt;/li&gt;
&lt;li&gt;A social post is &lt;strong&gt;viewed&lt;/strong&gt; millions of times, but &lt;strong&gt;created&lt;/strong&gt; once.&lt;/li&gt;
&lt;li&gt;A user updates their profile &lt;strong&gt;once or twice a year&lt;/strong&gt;, but their content is &lt;strong&gt;read hundreds of thousands of times a day&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A product's description changes &lt;strong&gt;a few times a year&lt;/strong&gt;, but it's &lt;strong&gt;browsed&lt;/strong&gt; thousands of times a day.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff8xmqm3gf0hfpz2p62zn.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff8xmqm3gf0hfpz2p62zn.jpg" alt="Comparison chart of the read-write asymmetry (giant " width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's a pattern here, and it's not a coincidence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Expensive updates and deletions are rare. Cheap operations are frequent.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the &lt;em&gt;nature of data models&lt;/em&gt;. Reading is what users do all day; writing is what happens occasionally. And critically — the operations that are &lt;em&gt;expensive to maintain&lt;/em&gt; in an embedded world (updating a user who is embedded in a thousand articles) are precisely the operations that &lt;em&gt;happen least often&lt;/em&gt; (a user updates their profile twice a year).&lt;/p&gt;

&lt;p&gt;Every framework optimizes reads. Few stop to notice that the write side — the side they're terrified of — is rare &lt;em&gt;because&lt;/em&gt; it's expensive, and cheap &lt;em&gt;because&lt;/em&gt; it's frequent. Lesan was designed around this reality, and that's what makes it elegant rather than hacky.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The best case: a blog
&lt;/h2&gt;

&lt;p&gt;Let's build a blog. The classic models: &lt;code&gt;user&lt;/code&gt;, &lt;code&gt;category&lt;/code&gt;, &lt;code&gt;tag&lt;/code&gt;, and &lt;code&gt;article&lt;/code&gt;. We'll focus on just &lt;code&gt;user&lt;/code&gt; and &lt;code&gt;article&lt;/code&gt; — the heart of any blog.&lt;/p&gt;

&lt;p&gt;With Lesan we declare the relationship once, on the side that needs it, and embedding happens on both sides automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;articleRelations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;schemaName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;single&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;relatedRelations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;multiple&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;desc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The effect: an &lt;strong&gt;article&lt;/strong&gt; embeds its &lt;strong&gt;author&lt;/strong&gt; (a pure snapshot of the user: &lt;code&gt;_id&lt;/code&gt;, name, avatar, bio), and a &lt;strong&gt;user&lt;/strong&gt; embeds their &lt;strong&gt;50 most recent articles&lt;/strong&gt;. Both sides stay in sync on every insert, update, and delete — automatically, with zero extra code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0vhcdr4ekktpw6v4ybjv.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0vhcdr4ekktpw6v4ybjv.jpg" alt="Entity-relationship diagram — " width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 Reads: this is where a blog lives
&lt;/h3&gt;

&lt;p&gt;A blog is a read machine. Look at all the ways an article is fetched:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;strong&gt;latest articles&lt;/strong&gt; (home feed),&lt;/li&gt;
&lt;li&gt;the &lt;strong&gt;best articles&lt;/strong&gt; (by score or votes),&lt;/li&gt;
&lt;li&gt;the &lt;strong&gt;most visited articles&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;related articles&lt;/strong&gt; (same category),&lt;/li&gt;
&lt;li&gt;the article page itself,&lt;/li&gt;
&lt;li&gt;the &lt;strong&gt;"more by this author"&lt;/strong&gt; strip at the bottom of an article (6–7 more articles by the same author).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And almost every one of these needs the &lt;strong&gt;author&lt;/strong&gt; next to the article — the byline, the avatar, the author page link.&lt;/p&gt;

&lt;p&gt;In a classic stack, each of those read paths needs a &lt;em&gt;join&lt;/em&gt; or a &lt;em&gt;second query&lt;/em&gt; for the author. With Lesan, the author's &lt;code&gt;_id&lt;/code&gt; and pure info are already embedded inside the article document. Every one of those read paths is served by the same snapshot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;author's &lt;code&gt;_id&lt;/code&gt; → &lt;strong&gt;instant&lt;/strong&gt; lookup of the author's full profile when needed,&lt;/li&gt;
&lt;li&gt;author's pure info → &lt;strong&gt;already there&lt;/strong&gt;, no query at all,&lt;/li&gt;
&lt;li&gt;the "more by this author" strip → served by the &lt;strong&gt;user's embedded &lt;code&gt;articles&lt;/code&gt; array&lt;/strong&gt; (50 newest), fetched in the same request as the article.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The read cost collapses to a handful of documents — the same O(log n) win from &lt;a href="https://medium.com/@hemedani/why-lesan-we-built-a-framework-that-turns-o-n%C2%B2-queries-into-o-log-n-72d031a5103f" rel="noopener noreferrer"&gt;part 1&lt;/a&gt;, applied to every single read path of a real blog.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 Writes: the cost is tiny because it's rare
&lt;/h3&gt;

&lt;p&gt;Now the part that scared us: when an article is &lt;strong&gt;updated&lt;/strong&gt; or &lt;strong&gt;deleted&lt;/strong&gt;, how many places does it live?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the article document itself, and&lt;/li&gt;
&lt;li&gt;the author's embedded &lt;code&gt;articles&lt;/code&gt; array — and even that is bounded: the array holds only the &lt;strong&gt;50 newest&lt;/strong&gt; articles, so Lesan touches it only when the changed article is actually inside that window. An article that has already fallen past the limit lives only in the &lt;code&gt;article&lt;/code&gt; collection — there is nothing to sync.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's &lt;strong&gt;two places&lt;/strong&gt;. And how often does an article change? Once when it's written, and maybe once more when it's edited. Meanwhile it's read &lt;strong&gt;a million times&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A million reads against two writes.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The balance is absurdly good. The sync Lesan does on those two rare writes is nothing compared to the reads it saves on every single view.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.3 The harder write: the user updates or deletes themselves
&lt;/h3&gt;

&lt;p&gt;The genuinely expensive scenario is the &lt;strong&gt;user&lt;/strong&gt; changing. Now every article embedding them must be refreshed. And that number could be large — a popular author with thousands of articles.&lt;/p&gt;

&lt;p&gt;But how large, really? Think about the &lt;em&gt;nature&lt;/em&gt; of a user:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A user who publishes &lt;strong&gt;every single day for a year&lt;/strong&gt; has at most &lt;strong&gt;365 articles&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Updating or deleting that user means touching up to &lt;strong&gt;365 embedded snapshots&lt;/strong&gt; — a bounded, knowable, one-time cost.&lt;/li&gt;
&lt;li&gt;Meanwhile, those same 365 articles are read &lt;strong&gt;hundreds of thousands of times a day&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Millions of reads every day, against 365 writes once a year.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's not a compromise. That's a &lt;em&gt;steal&lt;/em&gt;. The worst case for a blog is bounded by how many articles one person can realistically write — and that number is nothing compared to how often those articles are read.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.4 Why the blog is the "best case"
&lt;/h3&gt;

&lt;p&gt;The blog is the best case because it's the &lt;em&gt;typical&lt;/em&gt; case. Content platforms, news sites, documentation, forums, catalogs — they all have this shape:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a small set of &lt;strong&gt;content entities&lt;/strong&gt; (articles, products, posts) written rarely,&lt;/li&gt;
&lt;li&gt;each embedded in its parent (author, category, store) in a few bounded places,&lt;/li&gt;
&lt;li&gt;read &lt;strong&gt;constantly&lt;/strong&gt; in many different orderings (latest, best, most-visited, related).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wherever the write cost is real, the write frequency is minuscule. Lesan's embedding spends a little on the rare writes to save a lot on the constant reads.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The worst case: population statistics
&lt;/h2&gt;

&lt;p&gt;Now let's be honest — because if this article only showed the easy case, you'd be right to distrust it. There &lt;em&gt;is&lt;/em&gt; a scenario where embedding seems catastrophic. And studying it is exactly what teaches us the most.&lt;/p&gt;

&lt;p&gt;Imagine an app that stores population statistics: a &lt;code&gt;country&lt;/code&gt; model and a &lt;code&gt;human&lt;/code&gt; model.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The country is embedded inside each human (so a human always knows their country).&lt;/li&gt;
&lt;li&gt;The last ~50 humans are embedded inside each country (for first-page pagination; the rest come from the &lt;code&gt;human&lt;/code&gt; model on later pages, and the country is embedded in each human so the query stays optimal).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same pattern as the blog. Then disaster strikes: the country has a &lt;strong&gt;&lt;code&gt;population&lt;/code&gt; field&lt;/strong&gt;, and it changes &lt;strong&gt;every 5 seconds&lt;/strong&gt;. To keep the world consistent, every human of that country must have their embedded country snapshot refreshed. India. China. &lt;strong&gt;More than a billion updates every 5 seconds.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy35nmt7w6wwx4xt16ern.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy35nmt7w6wwx4xt16ern.jpg" alt="Diagram — a " width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the moment embedding looks like the worst idea in engineering history. And it's a &lt;em&gt;real&lt;/em&gt; concern — it's why many teams are terrified of denormalization.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1 Solution 1: exclude the volatile field
&lt;/h3&gt;

&lt;p&gt;The first fix is trivial. Tell Lesan not to embed &lt;code&gt;population&lt;/code&gt; into humans:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;humanRelations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;schemaName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;country&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;single&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;excludes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;population&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;   &lt;span class="c1"&gt;// don't copy this field into humans&lt;/span&gt;
    &lt;span class="na"&gt;relatedRelations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// humans embedded in country, last 50, as before&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now updating &lt;code&gt;population&lt;/code&gt; touches only the country document. The humans still know their country (name, flag, &lt;code&gt;_id&lt;/code&gt;), just not its live population. &lt;strong&gt;Problem solved.&lt;/strong&gt; Hooray.&lt;/p&gt;

&lt;p&gt;But let's not stop there — because there's a more interesting solution, and it's the one that reveals the heart of Lesan.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 Solution 2: let the nature of data guide you — have children
&lt;/h3&gt;

&lt;p&gt;Go back to the &lt;em&gt;essence&lt;/em&gt; of the model. What is &lt;code&gt;population&lt;/code&gt;, really?&lt;/p&gt;

&lt;p&gt;It isn't a stable property like a country's &lt;code&gt;name&lt;/code&gt; or &lt;code&gt;abb&lt;/code&gt;. It's a &lt;strong&gt;history&lt;/strong&gt; — a series of values changing over time. A field that changes every 5 seconds isn't a field; it's an entity that &lt;em&gt;wants&lt;/em&gt; to be born.&lt;/p&gt;

&lt;p&gt;So instead of a field, make it a &lt;strong&gt;model&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;populationRelations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;schemaName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;country&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;single&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;relatedRelations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;populations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;multiple&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;recordedAt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;desc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a country embeds its &lt;strong&gt;last 50 population records&lt;/strong&gt;, and&lt;/li&gt;
&lt;li&gt;each population record embeds its &lt;strong&gt;country&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both sides are stored together, synced automatically, and — crucially — the whole thing becomes &lt;em&gt;analytically rich&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;every population record carries its own &lt;strong&gt;timestamp&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;you can chart a country's population &lt;strong&gt;history&lt;/strong&gt;, not just its current number,&lt;/li&gt;
&lt;li&gt;you can compute &lt;strong&gt;deltas, trends, and statistics&lt;/strong&gt; from real data,&lt;/li&gt;
&lt;li&gt;and the human model's embedded country snapshot can simply &lt;code&gt;exclude&lt;/code&gt; the volatile data entirely.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkkkklp829ntxxqiethbs.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkkkklp829ntxxqiethbs.jpg" alt="Diagram — " width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Did you notice what happened? &lt;strong&gt;The Lesan mechanism itself guided you to a better, more professional model.&lt;/strong&gt; Instead of "country + a billion fragile embedded copies," you now have "country + a tidy &lt;code&gt;population&lt;/code&gt; model + humans" — each with clean, bounded relationships.&lt;/p&gt;

&lt;p&gt;This isn't a coincidence of this example. It's a documented principle in the &lt;a href="https://miaadteam.github.io/lesan/docs/concepts/what-is-the-relationship" rel="noopener noreferrer"&gt;Lesan docs on relationships&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Every frequently-changing field can become a relationship."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A field that changes often is a sign that a new model wants to be born. Lesan doesn't just tolerate that — it &lt;em&gt;encourages&lt;/em&gt; it, because its relationship engine makes the birth cheap and safe. The concept page put it beautifully: &lt;em&gt;"If the relationship leads to the birth of a child, both parties accept the relationship."&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4.3 The economics of the fix
&lt;/h3&gt;

&lt;p&gt;After the promotion, what does a write cost?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Insert a new population record: &lt;strong&gt;one insert&lt;/strong&gt; into the &lt;code&gt;population&lt;/code&gt; collection — plus the automatic embed into the country's last-50 array.&lt;/li&gt;
&lt;li&gt;No update to the country. No update to a billion humans.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the absolute worst case — compared to the naive design — you do &lt;strong&gt;two operations instead of one&lt;/strong&gt; (the insert plus the embed). Against &lt;strong&gt;millions of reads&lt;/strong&gt; for statistics and analytics, that is nothing.&lt;/p&gt;

&lt;p&gt;And here's the beautiful part: you didn't just &lt;em&gt;fix&lt;/em&gt; the problem. You &lt;em&gt;upgraded the model&lt;/em&gt;. The naive "update a population field" design could only ever answer "what's the population right now?" The model-design fix can answer "how has the population &lt;em&gt;changed&lt;/em&gt; over the last month?" — because the history now &lt;em&gt;exists&lt;/em&gt; as data.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. The general rule
&lt;/h2&gt;

&lt;p&gt;Put the two archetypes together and you get a practical design law you can apply to any project:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Deep, read-heavy graphs&lt;/strong&gt; (blog, catalog, map, feed, org chart) — embed the relations and enjoy O(log n) reads. The rare writes are cheap to maintain; the constant reads are where your users actually live.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Volatile fields&lt;/strong&gt; (population, price, stock, status) — don't embed them into a billion documents. Either &lt;strong&gt;&lt;code&gt;exclude&lt;/code&gt;&lt;/strong&gt; them from the snapshot, or &lt;strong&gt;promote them into their own model&lt;/strong&gt; and let Lesan maintain the relationship.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wherever writes are expensive, they are rare.&lt;/strong&gt; Design for the reads.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The nature of data models is on your side. All you have to do is notice it.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. The takeaway
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://medium.com/@hemedani/why-lesan-we-built-a-framework-that-turns-o-n%C2%B2-queries-into-o-log-n-72d031a5103f" rel="noopener noreferrer"&gt;part 1&lt;/a&gt; we showed that Lesan turns a ~2,550,250-document read into a ~25,250-document read. The obvious objection was &lt;em&gt;"but what about the writes?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This article is the answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In a blog, writing an article means syncing &lt;strong&gt;two places&lt;/strong&gt;, against &lt;strong&gt;a million reads&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Even the worst case — updating a user who published every day for a year — is &lt;strong&gt;365 writes once a year&lt;/strong&gt;, against &lt;strong&gt;millions of reads every day&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;And the truly volatile fields (population changing every 5 seconds) aren't embedding problems at all — they're a sign that &lt;strong&gt;a new model should be born&lt;/strong&gt;, and Lesan makes that birth elegant and safe.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lesan isn't fast because it's clever. &lt;strong&gt;It's fast because it understood the nature of data first.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you've read this far, try it: look at your own database, find your most-read entity, and ask "how many places does it live, and how often does each copy change?" You'll find the same asymmetry. That's the nature of data — and it's why Lesan feels like it was designed for the real world.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Resources&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Part 1 — &lt;a href="https://dev.to/syd_amir_5353ffa4816e3bc5/why-lesan-we-built-a-framework-that-turns-on2-queries-into-olog-n-1f45"&gt;Why Lesan: turning O(n²) into O(log n)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The philosophy — &lt;a href="https://miaadteam.github.io/lesan/docs/concepts/what-is-the-relationship" rel="noopener noreferrer"&gt;What Is the Relationship Really?&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Why MongoDB — &lt;a href="https://miaadteam.github.io/lesan/docs/concepts/why-nosql" rel="noopener noreferrer"&gt;Why NoSQL?&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Lesan on GitHub: &lt;a href="https://github.com/MiaadTeam/lesan" rel="noopener noreferrer"&gt;github.com/MiaadTeam/lesan&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://miaadteam.github.io/lesan/" rel="noopener noreferrer"&gt;miaadteam.github.io/lesan&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;License: AGPL-3.0&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>api</category>
    </item>
    <item>
      <title>Why Lesan: We Built a Framework That Turns O(n2) Queries Into O(log n)</title>
      <dc:creator>Syd Amir</dc:creator>
      <pubDate>Wed, 19 Aug 2026 11:01:04 +0000</pubDate>
      <link>https://dev.to/syd_amir_5353ffa4816e3bc5/why-lesan-we-built-a-framework-that-turns-on2-queries-into-olog-n-1f45</link>
      <guid>https://dev.to/syd_amir_5353ffa4816e3bc5/why-lesan-we-built-a-framework-that-turns-on2-queries-into-olog-n-1f45</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fezysuqorfa4s27lu7ma8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fezysuqorfa4s27lu7ma8.png" alt="cover image" width="800" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How one design decision — treating relationships as data that keeps itself in sync — makes Lesan return data hundreds of times faster than the stacks most of us use today.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;By the Lesan team&lt;/strong&gt; · &lt;a href="https://github.com/MiaadTeam/lesan" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; · &lt;a href="https://miaadteam.github.io/lesan/" rel="noopener noreferrer"&gt;Docs&lt;/a&gt; · &lt;a href="https://dev.to/syd_amir_5353ffa4816e3bc5/the-nature-of-data-why-lesans-relationships-win-in-the-real-world-291k"&gt;Read part 2&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The problem: a "simple" request that kills your API
&lt;/h2&gt;

&lt;p&gt;Imagine you run an application where users, provinces, and cities all belong to a country. Now the frontend asks for one screen:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Give me all 250 countries, and for each one: the 50 most recent users, the 50 oldest users, the 50 most populous provinces, and for each province the 50 most recent and 50 oldest users — and the same for the 50 most populous cities."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's not an exotic request. It's a dashboard. A map view. An admin panel. And yet, in the most popular stacks on earth, this single screen is a performance disaster.&lt;/p&gt;

&lt;p&gt;Let's count what has to happen:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;What you fetch&lt;/th&gt;
&lt;th&gt;Documents&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;250 countries, by population&lt;/td&gt;
&lt;td&gt;250&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;50 recent + 50 oldest users per country&lt;/td&gt;
&lt;td&gt;250 × 2 × 50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;50 populous provinces per country&lt;/td&gt;
&lt;td&gt;250 × 50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;50 recent + 50 oldest users per province&lt;/td&gt;
&lt;td&gt;250 × 50 × 2 × 50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;50 populous cities per country&lt;/td&gt;
&lt;td&gt;250 × 50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;50 recent + 50 oldest users per city&lt;/td&gt;
&lt;td&gt;250 × 50 × 2 × 50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;≈ 2,550,250 documents&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two and a half &lt;em&gt;million&lt;/em&gt; documents — collected, joined, sorted, and assembled — just to paint one screen. And if your backend is doing this in a nested loop of ORM queries, you're not only paying for the database work; you're paying for &lt;strong&gt;2.5 million network round-trips&lt;/strong&gt; between your server and your database.&lt;/p&gt;

&lt;p&gt;Now here's the uncomfortable question nobody in the "just use Prisma/Mongoose" world asks: &lt;strong&gt;why do we accept this as normal?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. What is Lesan?
&lt;/h2&gt;

&lt;p&gt;Lesan is a cross-platform &lt;strong&gt;TypeScript web framework + ODM&lt;/strong&gt; for MongoDB that runs on Node.js, Bun, and Deno from a single codebase. It gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Models&lt;/strong&gt; — pure fields + relations, validated with &lt;code&gt;superstruct&lt;/code&gt; at runtime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Acts&lt;/strong&gt; — your API endpoints, defined with per-act validators (&lt;code&gt;set&lt;/code&gt; for input, &lt;code&gt;get&lt;/code&gt; for output).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client-driven projections&lt;/strong&gt; — like GraphQL, the &lt;em&gt;client&lt;/em&gt; chooses which fields (and how deep) it wants back, with no query language, no schema introspection server, and no backend refactoring when the frontend needs more.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embedded, self-syncing relationships&lt;/strong&gt; — the part this article is about.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A request pipeline&lt;/strong&gt; (&lt;code&gt;POST /lesan&lt;/code&gt;) that validates the model → act → input, runs optional auth hooks, executes the act, and applies the projection — all over a single endpoint.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The same &lt;code&gt;lesan()&lt;/code&gt; factory, the same models, the same acts, run unmodified on Node.js, Bun, or Deno. There's no build step to install a runtime-specific backend: the framework is a thin layer over the official MongoDB driver plus the platform's native HTTP.&lt;/p&gt;

&lt;p&gt;Lesan is open source: &lt;a href="https://github.com/MiaadTeam/lesan" rel="noopener noreferrer"&gt;github.com/MiaadTeam/lesan&lt;/a&gt;. It's also used in production by real applications (including &lt;a href="https://ziwound.com/en" rel="noopener noreferrer"&gt;ZiWound&lt;/a&gt;, a war-crimes documentation platform with 13 models and 98 acts).&lt;/p&gt;

&lt;p&gt;But let's get to the heart of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The core idea: relationships as self-maintaining data
&lt;/h2&gt;

&lt;p&gt;Every modern web framework treats relationships as something you &lt;em&gt;compute&lt;/em&gt; at read time. You store foreign keys; when a client asks for a country with its provinces, the framework joins — in SQL with &lt;code&gt;JOIN&lt;/code&gt;s, in MongoDB with &lt;code&gt;$lookup&lt;/code&gt; or a nested &lt;code&gt;find&lt;/code&gt; loop.&lt;/p&gt;

&lt;p&gt;Lesan flips this. &lt;strong&gt;Relationships are one-directional in definition, but embedded bi-directionally in storage — and Lesan keeps the embedded copies in sync for you.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Concretely: when you define a &lt;code&gt;province&lt;/code&gt; that belongs to a &lt;code&gt;country&lt;/code&gt;, Lesan automatically embeds a snapshot of that province &lt;em&gt;inside the country document&lt;/em&gt;, and a snapshot of the country &lt;em&gt;inside the province document&lt;/em&gt;. When a province is inserted, updated, or deleted, Lesan updates every embedded copy automatically. You, the backend developer, write &lt;strong&gt;zero&lt;/strong&gt; sync code.&lt;/p&gt;

&lt;p&gt;Let's see what that looks like. This is the complete model definition from the article's &lt;a href="https://github.com/MiaadTeam/lesan/tree/main/examples/whyLesan" rel="noopener noreferrer"&gt;companion example&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;population&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;abb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;countryRelations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;countries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;coreApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;odm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;country&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;pure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;countryRelations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provinceRelations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;schemaName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;country&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;single&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;relatedRelations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;provinces&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;multiple&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;desc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;provincesByPopulation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;multiple&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;population&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;desc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provinces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;coreApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;odm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;province&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;pure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;provinceRelations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read the &lt;code&gt;relatedRelations&lt;/code&gt; block like a contract: &lt;em&gt;"when a province points at a country, that country gains two fields — &lt;code&gt;provinces&lt;/code&gt;, the 50 newest provinces, and &lt;code&gt;provincesByPopulation&lt;/code&gt;, the 50 most populous provinces."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Two embedded, pre-sorted, limited arrays. Kept in sync by Lesan. &lt;strong&gt;Two different orderings of the same data, stored and maintained for free.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsyly0h65sni19rjudygp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsyly0h65sni19rjudygp.png" alt="Diagram of a country document with two embedded province arrays" width="800" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why two arrays?
&lt;/h3&gt;

&lt;p&gt;Because in a real app you need &lt;em&gt;both&lt;/em&gt; orderings. The recent-items list (dashboard) and the by-population list (map). In a classic setup you'd write two queries per country. Here, both arrays already live inside the country document, already sorted, already limited — so reading them is a single document fetch with zero sorting at read time.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's actually stored, and what isn't
&lt;/h3&gt;

&lt;p&gt;Two details matter here, and both are easy to miss:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The back-reference arrays are stored on the &lt;em&gt;target&lt;/em&gt;, not the source.&lt;/strong&gt; &lt;code&gt;provinces&lt;/code&gt; and &lt;code&gt;provincesByPopulation&lt;/code&gt; live inside the &lt;em&gt;country&lt;/em&gt; document. The province document itself only stores a &lt;code&gt;country&lt;/code&gt; snapshot. Lesan builds this from a single declaration — the province says "I belong to a country," and Lesan derives the reverse side automatically. This is the opposite of how most ODMs work, where you'd hand-maintain &lt;code&gt;country.provinces&lt;/code&gt; yourself.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Each embedded entry is a pure projection, not a full document.&lt;/strong&gt; Lesan embeds only the &lt;em&gt;pure&lt;/em&gt; fields of the province (plus the relation's declared &lt;code&gt;excludes&lt;/code&gt;). You decide what a province snapshot carries when you define the model. That's what keeps a &lt;code&gt;provinces&lt;/code&gt; array of 50 small, and what keeps the country document far below MongoDB's 16 MB limit even as it accumulates several such arrays.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Because the embedded copies are pure projections, the system also has a bounded, predictable cost model: &lt;code&gt;limit&lt;/code&gt; caps array length, &lt;code&gt;excludes&lt;/code&gt; caps entry size, and the number of back-references is fixed at model-definition time. No hidden growth, no surprise bloat.&lt;/p&gt;

&lt;h3&gt;
  
  
  The "one step less depth" principle
&lt;/h3&gt;

&lt;p&gt;Lesan's read path has a simple rule: &lt;strong&gt;for each request, penetrate one level less than a naive traversal would.&lt;/strong&gt; In a classic stack, fetching countries → provinces → cities → users means at &lt;em&gt;each&lt;/em&gt; level you issue a query against the child collection. With embedded snapshots, the children are already inside the parent document — so the deep query that would touch three collections instead reads one collection (the countries) and lets the projection engine resolve the embedded levels that are already present. The result is that a relationship &lt;em&gt;that used to require a round-trip now costs nothing at read time&lt;/em&gt; — it was paid for, once, at write time.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The magic, in code: insert a province, and everything updates
&lt;/h2&gt;

&lt;p&gt;Now the &lt;em&gt;writes&lt;/em&gt;. Adding a country needs no relation code at all:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addCountryValidator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pure&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;coreApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;schemas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;selectStruct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;country&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addCountry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ActFn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;population&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;abb&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;countries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;population&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;abb&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;projection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;coreApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;acts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAct&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;country&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;actName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;addCountry&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;addCountryValidator&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;addCountry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwfvssq4s7zgm8n6l4n2o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwfvssq4s7zgm8n6l4n2o.png" alt="Lesan Playground: adding a country" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adding a province takes the country's &lt;code&gt;_id&lt;/code&gt; and — that's it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addProvinceValidator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;pure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;countryId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;objectIdValidation&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;coreApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;schemas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;selectStruct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;province&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addProvince&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ActFn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;population&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;abb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;countryId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;provinces&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;population&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;abb&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;relations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;_ids&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countryId&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;relatedRelations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;provinces&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;provincesByPopulation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;projection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;coreApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;acts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAct&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;province&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;actName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;addProvince&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;addProvinceValidator&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;addProvince&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the &lt;em&gt;entire&lt;/em&gt; relation-handling code for inserting a province: declare the target, declare which &lt;code&gt;relatedRelations&lt;/code&gt; to refresh, done. Lesan:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Inserts the province document.&lt;/li&gt;
&lt;li&gt;Embeds a province snapshot inside the country's &lt;code&gt;provinces&lt;/code&gt; array (positioned by &lt;code&gt;_id&lt;/code&gt; sort).&lt;/li&gt;
&lt;li&gt;Embeds a province snapshot inside the country's &lt;code&gt;provincesByPopulation&lt;/code&gt; array (positioned by &lt;code&gt;population&lt;/code&gt; sort).&lt;/li&gt;
&lt;li&gt;Embeds a country snapshot inside the province (the forward side).&lt;/li&gt;
&lt;li&gt;Evicts the 51st-oldest entries to respect each &lt;code&gt;limit: 50&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The same thing happens on &lt;code&gt;update&lt;/code&gt; and &lt;code&gt;delete&lt;/code&gt; — automatically, with zero extra code.&lt;/strong&gt; If the province's population changes, Lesan re-sorts &lt;code&gt;provincesByPopulation&lt;/code&gt;. If the province is deleted, both arrays are repaired. No &lt;code&gt;arrayFilters&lt;/code&gt;, no &lt;code&gt;updateMany&lt;/code&gt;, no sagas, no sync jobs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbx3rgbhngdyd2tnrpbv9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbx3rgbhngdyd2tnrpbv9.png" alt="Mongo Compass: a country document with the embedded, sorted arrays" width="799" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The embedded snapshots are also &lt;strong&gt;pure projections&lt;/strong&gt; (just the fields you care about, trimmed via &lt;code&gt;excludes&lt;/code&gt;) — so a 50-item array of full provinces never bloat a country document past MongoDB's 16 MB ceiling.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. The numbers: O(n²) becomes O(log n)
&lt;/h2&gt;

&lt;p&gt;Here's the payoff. Revisit the million-document request from section 1 — now against Lesan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The 250 countries are &lt;strong&gt;250 documents&lt;/strong&gt; — one query.&lt;/li&gt;
&lt;li&gt;Each country document &lt;em&gt;already contains&lt;/em&gt; its 50 recent users, 50 oldest users, 50 populous provinces, 50 populous cities — &lt;strong&gt;embedded and pre-sorted&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So fetching the whole tree is &lt;strong&gt;just the 250 country documents&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stack&lt;/th&gt;
&lt;th&gt;Documents fetched&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL (nested queries / ORM)&lt;/td&gt;
&lt;td&gt;~2,550,250&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MongoDB (nested &lt;code&gt;find&lt;/code&gt; loop)&lt;/td&gt;
&lt;td&gt;~2,550,250&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MongoDB (&lt;code&gt;$lookup&lt;/code&gt; pipelines)&lt;/td&gt;
&lt;td&gt;~2,550,250 (one pipeline, but still massive join work)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lesan&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;25,250&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Wait — why 25,250 and not 250? Because at &lt;code&gt;selectStruct("country", 2)&lt;/code&gt; depth 2, you ask for countries → provinces/cities → users. The users embedded &lt;em&gt;inside&lt;/em&gt; provinces/cities are fetched by Lesan's projection engine at that deeper level. Still — 25,250 documents instead of 2,550,250 is a &lt;strong&gt;~100× reduction&lt;/strong&gt;, and those 25,250 come back in a handful of database round-trips instead of thousands.&lt;/p&gt;

&lt;p&gt;In complexity terms: a naive N+1-style traversal is &lt;code&gt;O(n²)&lt;/code&gt; (actually &lt;code&gt;O(n^depth)&lt;/code&gt; — with three levels of nesting, it's &lt;code&gt;n^3&lt;/code&gt;). Lesan's embedded model reduces the &lt;em&gt;collection&lt;/em&gt; work to &lt;code&gt;O(log n)&lt;/code&gt; — the embedded arrays are just fields of a document you already fetched. More importantly, it removes the &lt;strong&gt;2.5 million network round-trips&lt;/strong&gt; between server and database, which is where most of your latency actually lives.&lt;/p&gt;

&lt;p&gt;Let me be careful about what &lt;code&gt;O(log n)&lt;/code&gt; means here, because a reviewer will (rightly) poke at it. The claim isn't that MongoDB has magical indexing on embedded arrays. The claim is about &lt;em&gt;your code's&lt;/em&gt; data-collection cost: the number of documents your application must fetch and assemble to answer a deep request. In a nested-query stack that number grows with the &lt;em&gt;product&lt;/em&gt; of the depth and the fan-out (&lt;code&gt;n × m × k&lt;/code&gt;). In Lesan, the deep levels are already inside the documents you fetch at the top level — so the &lt;em&gt;application-level&lt;/em&gt; cost of that same request is dominated by the top-level documents, which scales as &lt;code&gt;O(log n)&lt;/code&gt; (a handful of index hits). MongoDB's index on &lt;code&gt;_id&lt;/code&gt; (and on any &lt;code&gt;createIndex&lt;/code&gt; you declare) is what makes each of those lookups logarithmic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where the "25,250" comes from
&lt;/h3&gt;

&lt;p&gt;In the example above I set &lt;code&gt;selectStruct("country", 2)&lt;/code&gt; — depth 2 — which means: countries at the top level, provinces/cities at depth 1 (already embedded in the country documents), and their users at depth 2 (already embedded in the province/city snapshots). Because every level below the top is embedded, the only &lt;em&gt;collection&lt;/em&gt; query is for the 250 country documents; the users inside provinces and cities are resolved from the already-fetched snapshots by the projection engine. So:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;250 country documents&lt;/strong&gt; — one indexed query.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;25,000 users&lt;/strong&gt; — already inside those 250 documents as embedded snapshots.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Total application-level work: &lt;strong&gt;25,250 documents&lt;/strong&gt;. Same screen, same data, ~100× less fetching.&lt;/p&gt;

&lt;p&gt;And here's the part that matters even more than the document count: the classic stack doesn't just fetch 2.5M documents — it fetches them as &lt;strong&gt;2.5M sequential round-trips&lt;/strong&gt; in an N+1 loop (or one giant pipeline that materializes all of them in memory). The dominant cost in real systems is usually that per-query overhead and the wire time, not the documents themselves. Lesan collapses 2.5M round-trips into one query.&lt;/p&gt;

&lt;p&gt;Let's make this concrete with the &lt;em&gt;read&lt;/em&gt; act:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getCountriesValidator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({}),&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;coreApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;schemas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;selectStruct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;country&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getCountries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ActFn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;countries&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;aggregation&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="na"&gt;projection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;coreApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;acts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAct&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;country&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;actName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getCountries&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getCountriesValidator&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;getCountries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The backend act is &lt;em&gt;tiny&lt;/em&gt; — because the client decides what to fetch. A single POST carries the projection tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;http://localhost:&lt;/span&gt;&lt;span class="mi"&gt;7500&lt;/span&gt;&lt;span class="err"&gt;/lesan&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"act"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"getCountries"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"set"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"page"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"get"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"population"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"provincesByPopulation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"population"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"family"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"usersByAge"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"family"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The client asks, Lesan answers. No new endpoint, no backend refactor, no GraphQL schema to maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. What you no longer have to write
&lt;/h2&gt;

&lt;p&gt;The draft of this article contained a very long section on what manual embedding costs. Let me show you &lt;em&gt;why&lt;/em&gt; Lesan exists, by showing you the alternative.&lt;/p&gt;

&lt;p&gt;If you embed data yourself, you must keep it in sync &lt;strong&gt;by hand&lt;/strong&gt;. Here is a &lt;em&gt;minimal&lt;/em&gt; version of updating one user across the nested arrays of just two collections — using MongoDB's &lt;code&gt;updateMany&lt;/code&gt; with &lt;code&gt;arrayFilters&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// WITHOUT Lesan: update one user across every place they're embedded&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;updateMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;recentUsers.userId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;$set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;recentUsers.$[elem]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;updatedUserData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;arrayFilters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;elem.userId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;updateMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;oldestUsers.userId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;$set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;oldestUsers.$[elem]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;updatedUserData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;arrayFilters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;elem.userId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;updateMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;provinces.recentUsers.userId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;$set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;provinces.$[].recentUsers.$[elem]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;updatedUserData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;arrayFilters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;elem.userId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;countries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;updateMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;provinces.oldestUsers.userId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;$set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;provinces.$[].oldestUsers.$[elem]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;updatedUserData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;arrayFilters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;elem.userId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now multiply that by: cities, the forward-side snapshots, the second (by-population) ordering, deletes, and the subtle bugs where a user moves between provinces and their old snapshot lingers. This is exactly the class of bug that produces &lt;strong&gt;stale data in production&lt;/strong&gt; — and it's why most teams simply give up on embedding and go back to joins.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why the manual approach always breaks
&lt;/h3&gt;

&lt;p&gt;Three structural problems doom hand-rolled embedding at scale:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The update fan-out is unbounded.&lt;/strong&gt; Every write must find &lt;em&gt;every&lt;/em&gt; document that could contain a snapshot — which is every document in every collection that ever referenced this entity. You can't &lt;code&gt;$set&lt;/code&gt; your way out of a 50-item sorted array that needs re-sorting; you have to &lt;em&gt;remove&lt;/em&gt; the stale entry and &lt;em&gt;re-insert&lt;/em&gt; the updated one in the right position, for every ordering you maintain, across every referencing document.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Delete is the worst case.&lt;/strong&gt; Removing a province means walking every country, every ordering, and any parent that transitively embedded it. Miss one spot and you've shipped a phantom record to your users.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Consistency is eventual — at best.&lt;/strong&gt; There's no transaction across your N &lt;code&gt;updateMany&lt;/code&gt; calls. A crash mid-way leaves your data in a half-updated state with no reconciliation job.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Lesan removes this entire class of code and these failure modes. Update the user document once; every embedded copy is re-projected automatically. The &lt;a href="https://github.com/MiaadTeam/lesan" rel="noopener noreferrer"&gt;update/delete sync engine&lt;/a&gt; handles forward + reverse propagation, sort-window maintenance, and limit eviction — tested, not trusted.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. The real benchmarks
&lt;/h2&gt;

&lt;p&gt;The query-count argument is architectural. But we also ran actual head-to-head benchmarks against real stacks, and the results back up the claim. You can reproduce everything in the &lt;a href="https://github.com/MiaadTeam/benchmark" rel="noopener noreferrer"&gt;MiaadTeam/benchmark&lt;/a&gt; repo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Client response time — vs the same request on other stacks
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stack&lt;/th&gt;
&lt;th&gt;Lesan is faster by&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;vs &lt;code&gt;prisma-express-rest&lt;/code&gt; (PostgreSQL)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1,168%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vs &lt;code&gt;prisma-express-graphql&lt;/code&gt; (PostgreSQL)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1,417%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vs &lt;code&gt;mongoose-express-rest&lt;/code&gt; (unsorted)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4,435%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vs &lt;code&gt;mongo-express-rest&lt;/code&gt; (unsorted)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;72,289%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vs &lt;code&gt;mongoose-express-rest&lt;/code&gt; (with sort)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;298,971%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Formula: &lt;code&gt;(B − A) ÷ A × 100&lt;/code&gt;. The chart lives in the repo: &lt;a href="https://github.com/MiaadTeam/lesan/blob/main/chart.svg" rel="noopener noreferrer"&gt;chart.svg&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Honesty note:&lt;/strong&gt; these are relative numbers. &lt;code&gt;298,971%&lt;/code&gt; sounds absurd because the baseline (&lt;code&gt;mongoose-express-rest&lt;/code&gt; with a sort over a nested traversal) is &lt;em&gt;catastrophically&lt;/em&gt; slow — that's the point. The absolute takeaway is the same: nested-query stacks pay for the same data many times over, and Lesan pays once.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  HTTP throughput — Lesan's own server
&lt;/h3&gt;

&lt;p&gt;Lesan's server handles over &lt;strong&gt;10,000 requests/second&lt;/strong&gt; on Bun and Deno, with sub-10 ms latency — because it's just the platform's native HTTP on top of the official MongoDB driver. (Full cross-runtime numbers in the &lt;a href="https://miaadteam.github.io/lesan/docs/benchmarks" rel="noopener noreferrer"&gt;benchmarks doc&lt;/a&gt;.)&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Tradeoffs: when NOT to use Lesan
&lt;/h2&gt;

&lt;p&gt;No engineering decision is free, and the HN/Reddit crowd will (correctly) push on this. Here's the honest version.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Embedding trades write cost for read cost.&lt;/strong&gt; Every insert/update/delete that touches a relationship touches the embedded copies too. For write-heavy workloads where the reads don't need the depth, this is extra work. Lesan mitigates it with &lt;code&gt;limit&lt;/code&gt;, &lt;code&gt;excludes&lt;/code&gt;, and pure projections — but it's a real consideration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Duplication is real.&lt;/strong&gt; The same province appears in &lt;code&gt;provinces&lt;/code&gt;, &lt;code&gt;provincesByPopulation&lt;/code&gt;, and its own collection. That's storage overhead — but each embedded copy is a &lt;em&gt;pure projection&lt;/em&gt; (a trimmed snapshot), not a full document. In practice the duplication is a small fraction of total size, and it's the price of O(log n) reads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The 16 MB document ceiling.&lt;/strong&gt; MongoDB documents cap at 16 MB. Lesan's &lt;code&gt;limit&lt;/code&gt; on every back-reference is exactly what keeps a country from accumulating 100,000 embedded provinces. Design your &lt;code&gt;limit&lt;/code&gt;/&lt;code&gt;excludes&lt;/code&gt; and you never approach the ceiling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;High-cardinality reverse lists.&lt;/strong&gt; If one document legitimately needs &lt;em&gt;all&lt;/em&gt; 100,000 children embedded, embedding is the wrong tool — keep the back-reference limited and query the child collection with an index instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When embedding is overkill.&lt;/strong&gt; If your relations are shallow and your reads are simple single-document fetches, Lesan still works perfectly — it just won't be the dramatic win. The win is proportional to &lt;em&gt;relationship depth&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The maintenance cost of the sync engine itself.&lt;/strong&gt; Every relationship operation runs Lesan's relation pipeline (forward + reverse projection, sort windows, limit eviction). For most workloads this is negligible next to the read savings — but it's real. Lesan's answer is to keep the pipeline small, index-driven, and to let you tune &lt;code&gt;limit&lt;/code&gt;/&lt;code&gt;excludes&lt;/code&gt; so each sync touches as little data as possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  When is Lesan the right choice?
&lt;/h3&gt;

&lt;p&gt;In practice the profile that wins is: &lt;strong&gt;deep, read-heavy relationship graphs with bounded fan-out per node&lt;/strong&gt; — exactly the shape of dashboards, content trees, map views, org charts, catalogs, and social feeds. If you're building one of those, the tradeoff is heavily in Lesan's favor: you trade a little extra write work and storage for reads that are an order of magnitude cheaper to fetch and, more importantly, an order of magnitude fewer round-trips.&lt;/p&gt;

&lt;p&gt;If your workload is write-heavy with shallow reads — a write-only event logger, a pure time-series ingest — embedding buys you little, and Lesan won't be dramatically faster than anything else. It still runs correctly and cross-platform; it just won't be the story of this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Run it yourself
&lt;/h2&gt;

&lt;p&gt;Everything in this article runs on your machine in minutes. The full example lives in the repo at &lt;a href="https://github.com/MiaadTeam/lesan/tree/main/examples/whyLesan" rel="noopener noreferrer"&gt;&lt;code&gt;examples/whyLesan/performance.ts&lt;/code&gt;&lt;/a&gt; — all four models, the embedded relations, and the acts, exactly as written above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# requires MongoDB + Deno (or Node/Bun)&lt;/span&gt;
deno run &lt;span class="nt"&gt;-A&lt;/span&gt; examples/whyLesan/performance.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open the Lesan &lt;strong&gt;Playground&lt;/strong&gt;, add a country, add a province with a &lt;code&gt;countryId&lt;/code&gt;, and inspect the country document in Mongo Compass — you'll see &lt;code&gt;provinces&lt;/code&gt; and &lt;code&gt;provincesByPopulation&lt;/code&gt;, both already sorted and limited. Then call &lt;code&gt;getCountries&lt;/code&gt; with the projection above and watch the whole tree come back in one shot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbxc3fob3vxytuoo5h4te.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbxc3fob3vxytuoo5h4te.png" alt="Lesan Playground: adding a province" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Conclusion
&lt;/h2&gt;

&lt;p&gt;The database industry spent decades optimizing &lt;em&gt;queries&lt;/em&gt;. Lesan's bet is different: &lt;strong&gt;if the data you need is already embedded, sorted, and in the document, the query barely has to exist.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Relationships are &lt;strong&gt;one-directional in definition&lt;/strong&gt;, &lt;strong&gt;embedded bi-directionally in storage&lt;/strong&gt;, and &lt;strong&gt;self-syncing&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Reads drop from ~2.5 million documents to ~25 thousand for the same screen — from &lt;code&gt;O(n²)&lt;/code&gt;-style nested traversals to &lt;code&gt;O(log n)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;You stop writing &lt;code&gt;updateMany&lt;/code&gt; + &lt;code&gt;arrayFilters&lt;/code&gt; sync code, and you stop shipping stale-data bugs.&lt;/li&gt;
&lt;li&gt;It's &lt;strong&gt;cross-platform&lt;/strong&gt; (Node.js / Bun / Deno), &lt;strong&gt;GraphQL-like&lt;/strong&gt; (client-driven projections), and &lt;strong&gt;open source&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We built Lesan because we were tired of watching dashboards do 2.5 million queries to paint one screen. If that pain sounds familiar — try it, benchmark it against your own stack, and &lt;a href="https://github.com/MiaadTeam/lesan/issues" rel="noopener noreferrer"&gt;open an issue&lt;/a&gt; if you think we're wrong. We'd love to be proven right.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Resources&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read this article on Medium: &lt;a href="https://medium.com/@hemedani/why-lesan-we-built-a-framework-that-turns-o-n%C2%B2-queries-into-o-log-n-72d031a5103f" rel="noopener noreferrer"&gt;Why Lesan: We Built a Framework That Turns O(n²) Queries Into O(log n)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Part 2 — &lt;a href="https://dev.to/syd_amir_5353ffa4816e3bc5/the-nature-of-data-why-lesans-relationships-win-in-the-real-world-291k"&gt;The Nature of Data: Why Lesan's Relationships Win in the Real World&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Lesan on GitHub: &lt;a href="https://github.com/MiaadTeam/lesan" rel="noopener noreferrer"&gt;github.com/MiaadTeam/lesan&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://miaadteam.github.io/lesan/" rel="noopener noreferrer"&gt;miaadteam.github.io/lesan&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Benchmark repo (reproduce everything): &lt;a href="https://github.com/MiaadTeam/benchmark" rel="noopener noreferrer"&gt;github.com/MiaadTeam/benchmark&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Companion example: &lt;a href="https://github.com/MiaadTeam/lesan/tree/main/examples/whyLesan" rel="noopener noreferrer"&gt;&lt;code&gt;examples/whyLesan&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtu.be/FzMNIGanXSQ" rel="noopener noreferrer"&gt;Intro video (Farsi)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;License: AGPL-3.0&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>mongodb</category>
      <category>typescript</category>
      <category>database</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
