docs: collect refreshed mongo-ops wiki site (API-correct overhaul, v0.1.5)

This commit is contained in:
2026-09-14 13:46:11 +05:30
parent bbd9863d23
commit f406aa2f21
24 changed files with 2880 additions and 1525 deletions

View File

@@ -20,7 +20,7 @@
<title>02 components - Mongo Ops Documentation</title>
<title>Core Components - Mongo Ops Documentation</title>
@@ -108,7 +108,7 @@
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
02 components
Core Components
</span>
</div>
@@ -458,13 +458,15 @@
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
<label class="md-nav__link md-nav__link--active" for="__toc">
<span class="md-ellipsis">
02 components
Core Components
</span>
@@ -477,7 +479,7 @@
<span class="md-ellipsis">
02 components
Core Components
</span>
@@ -490,6 +492,8 @@
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
@@ -498,16 +502,6 @@
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#core-components" class="md-nav__link">
<span class="md-ellipsis">
Core Components
</span>
</a>
<nav class="md-nav" aria-label="Core Components">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#1-mongoconnectionmanager" class="md-nav__link">
<span class="md-ellipsis">
1. MongoConnectionManager
@@ -515,93 +509,120 @@
</a>
</li>
<li class="md-nav__item">
<a href="#2-basedocument" class="md-nav__link">
<li class="md-nav__item">
<a href="#2-basedocument-pyobjectid" class="md-nav__link">
<span class="md-ellipsis">
2. BaseDocument
2. BaseDocument &amp; PyObjectId
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#3-baserepositoryt" class="md-nav__link">
<li class="md-nav__item">
<a href="#3-crudmixint" class="md-nav__link">
<span class="md-ellipsis">
3. BaseRepository[T]
3. CRUDMixin[T]
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#4-transactionmanager" class="md-nav__link">
<li class="md-nav__item">
<a href="#4-baserepositoryt" class="md-nav__link">
<span class="md-ellipsis">
4. TransactionManager
4. BaseRepository[T]
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#5-modelregistry" class="md-nav__link">
<li class="md-nav__item">
<a href="#5-populatingrepositoryt" class="md-nav__link">
<span class="md-ellipsis">
5. ModelRegistry
5. PopulatingRepository[T]
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#6-cache-backend" class="md-nav__link">
<li class="md-nav__item">
<a href="#6-transactionmanager" class="md-nav__link">
<span class="md-ellipsis">
6. Cache Backend
6. TransactionManager
</span>
</a>
<nav class="md-nav" aria-label="6. Cache Backend">
</li>
<li class="md-nav__item">
<a href="#7-modelregistry" class="md-nav__link">
<span class="md-ellipsis">
7. ModelRegistry
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#8-cache-layer" class="md-nav__link">
<span class="md-ellipsis">
8. Cache Layer
</span>
</a>
<nav class="md-nav" aria-label="8. Cache Layer">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#61-cachebackend-abstract" class="md-nav__link">
<a href="#81-cachebackend-abstract" class="md-nav__link">
<span class="md-ellipsis">
6.1 CacheBackend (abstract)
8.1 CacheBackend (abstract)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#62-cacheconfig" class="md-nav__link">
<a href="#82-cachestats" class="md-nav__link">
<span class="md-ellipsis">
6.2 CacheConfig
8.2 CacheStats
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#63-inmemorycachebackend" class="md-nav__link">
<a href="#83-cacheconfig" class="md-nav__link">
<span class="md-ellipsis">
6.3 InMemoryCacheBackend
8.3 CacheConfig
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#64-rediscachebackend" class="md-nav__link">
<a href="#84-inmemorycachebackend" class="md-nav__link">
<span class="md-ellipsis">
6.4 RedisCacheBackend
8.4 InMemoryCacheBackend
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#65-cachedbaserepositoryt" class="md-nav__link">
<a href="#85-rediscachebackend" class="md-nav__link">
<span class="md-ellipsis">
6.5 CachedBaseRepository[T]
8.5 RedisCacheBackend
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#86-cachedbaserepositoryt" class="md-nav__link">
<span class="md-ellipsis">
8.6 CachedBaseRepository[T]
</span>
</a>
@@ -611,39 +632,39 @@
</nav>
</li>
<li class="md-nav__item">
<a href="#7-population-engine" class="md-nav__link">
<li class="md-nav__item">
<a href="#9-population-layer" class="md-nav__link">
<span class="md-ellipsis">
7. Population Engine
9. Population Layer
</span>
</a>
<nav class="md-nav" aria-label="7. Population Engine">
<nav class="md-nav" aria-label="9. Population Layer">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#71-populaterule" class="md-nav__link">
<a href="#91-populaterule" class="md-nav__link">
<span class="md-ellipsis">
7.1 PopulateRule
9.1 PopulateRule
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#72-populationengine" class="md-nav__link">
<a href="#92-populationengine" class="md-nav__link">
<span class="md-ellipsis">
7.2 PopulationEngine
9.2 PopulationEngine
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#73-populatingrepositoryt" class="md-nav__link">
<a href="#93-circularreferenceerrorvalueerror" class="md-nav__link">
<span class="md-ellipsis">
7.3 PopulatingRepository[T]
9.3 CircularReferenceError(ValueError)
</span>
</a>
@@ -653,9 +674,13 @@
</nav>
</li>
</ul>
</nav>
<li class="md-nav__item">
<a href="#related" class="md-nav__link">
<span class="md-ellipsis">
Related
</span>
</a>
</li>
@@ -1157,100 +1182,252 @@
<h1>02 components</h1>
<h2 id="core-components">Core Components</h2>
<h3 id="1-mongoconnectionmanager">1. MongoConnectionManager</h3>
<p>Manages MongoDB connections with async lifecycle.
<strong>Methods:</strong></p>
<h1 id="core-components">Core Components</h1>
<p>A validated reference to the public API. Signatures match the code in <code>mongo_ops/</code> exactly. See the use cases for runnable recipes.</p>
<hr />
<h2 id="1-mongoconnectionmanager">1. MongoConnectionManager</h2>
<p>Singleton-style owner of the Motor client and active database.</p>
<table>
<thead>
<tr>
<th>Method</th>
<th>Signature</th>
<th>Behavior</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>connect</code></td>
<td><code>async (uri: str, db_name: str, **kwargs) -&gt; AsyncIOMotorDatabase</code></td>
<td>Creates <code>AsyncIOMotorClient(uri, **kwargs)</code> and selects the database. Idempotent.</td>
</tr>
<tr>
<td><code>disconnect</code></td>
<td><code>async () -&gt; None</code></td>
<td>Closes the client and clears state.</td>
</tr>
<tr>
<td><code>get_database</code></td>
<td><code>() -&gt; AsyncIOMotorDatabase</code></td>
<td>Returns the active database; raises <code>RuntimeError("Database not connected. Call connect() first.")</code>.</td>
</tr>
<tr>
<td><code>get_client</code></td>
<td><code>() -&gt; AsyncIOMotorClient</code></td>
<td>Returns the active client; raises <code>RuntimeError("Client not connected. Call connect() first.")</code>.</td>
</tr>
<tr>
<td><code>lifespan</code></td>
<td><code>async ctx manager (uri, db_name, **kwargs)</code></td>
<td><code>connect()</code> on entry, yields the database, <code>disconnect()</code> on exit. Designed for FastAPI lifespan.</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="2-basedocument-pyobjectid">2. BaseDocument &amp; PyObjectId</h2>
<p><code>BaseDocument(BaseModel)</code> — inherit for every Mongo entity.</p>
<ul>
<li><code>connect(uri, db_name, **kwargs)</code> - Connect to MongoDB</li>
<li><code>disconnect()</code> - Close connection</li>
<li><code>get_database()</code> - Get current database instance</li>
<li><code>get_client()</code> - Get current client instance</li>
<li><code>lifespan(uri, db_name, **kwargs)</code> - Context manager for FastAPI lifespan</li>
<li><code>id: Optional[PyObjectId]</code> — aliased to <code>_id</code>, serialized to <code>str</code>.</li>
<li><code>created_at: datetime</code> — defaults to <code>datetime.utcnow()</code>.</li>
<li><code>updated_at: datetime</code> — defaults to <code>datetime.utcnow()</code>.</li>
<li><code>Config.populate_by_name = True</code>, <code>arbitrary_types_allowed = True</code>, <code>json_encoders = {ObjectId: str}</code>.</li>
</ul>
<h3 id="2-basedocument">2. BaseDocument</h3>
<p>Base model for all MongoDB documents. Provides:</p>
<p><code>PyObjectId(ObjectId)</code> — Pydantic v2-compatible <code>ObjectId</code> that accepts <code>str</code> or <code>ObjectId</code> and validates with <code>ObjectId.is_valid</code>.</p>
<hr />
<h2 id="3-crudmixint">3. CRUDMixin[T]</h2>
<p>Generic CRUD over a Motor collection. The building block of all repositories.</p>
<table>
<thead>
<tr>
<th>Method</th>
<th>Signature</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>create</code></td>
<td><code>async (data: T) -&gt; T</code></td>
<td>Dumps model (excludes <code>id</code>, <code>None</code>), stamps <code>created_at</code>/<code>updated_at</code>, inserts, returns model with assigned <code>_id</code>.</td>
</tr>
<tr>
<td><code>get_by_id</code></td>
<td><code>async (id: str \| ObjectId) -&gt; Optional[T]</code></td>
<td><code>str</code> is accepted and converted to <code>ObjectId</code>.</td>
</tr>
<tr>
<td><code>get_many</code></td>
<td><code>async (filter: dict \| None = None, skip: int = 0, limit: int = 100, sort: list[tuple] \| None = None) -&gt; list[T]</code></td>
<td>Cursor <code>.skip().limit().sort(...)</code> then <code>to_list(limit)</code>. <code>limit=0</code> disables the limit clause.</td>
</tr>
<tr>
<td><code>update</code></td>
<td><code>async (id, data: dict[str, Any]) -&gt; Optional[T]</code></td>
<td><code>$set</code> + refreshed <code>updated_at</code> via <code>find_one_and_update</code>.</td>
</tr>
<tr>
<td><code>patch</code></td>
<td><code>async (id, data: dict[str, Any]) -&gt; Optional[T]</code></td>
<td>Same as <code>update</code> but intended for REST PATCH semantics.</td>
</tr>
<tr>
<td><code>delete</code></td>
<td><code>async (id) -&gt; bool</code></td>
<td>True if a document was deleted.</td>
</tr>
<tr>
<td><code>count</code></td>
<td><code>async (filter: dict \| None = None) -&gt; int</code></td>
<td><code>count_documents</code>.</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="4-baserepositoryt">4. BaseRepository[T]</h2>
<p><code>BaseRepository(collection_name: str, model: type[T])</code> — resolves the collection from <code>MongoConnectionManager.get_database()[collection_name]</code>. <strong>Requires an active connection at construction time.</strong> Provides everything in <code>CRUDMixin</code> plus <code>collection_name</code>.</p>
<hr />
<h2 id="5-populatingrepositoryt">5. PopulatingRepository[T]</h2>
<p><code>PopulatingRepository(collection_name, model, population_engine: PopulationEngine | None = None, populate_rules: list[PopulateRule] | None = None)</code>.</p>
<ul>
<li><code>id</code> (aliased to <code>_id</code>) - ObjectId</li>
<li><code>created_at</code> - Auto-generated timestamp</li>
<li><code>updated_at</code> - Auto-updated timestamp</li>
<li><code>set_population_engine(engine)</code> / <code>set_populate_rules(rules)</code> — swap engine/rules at runtime.</li>
<li><code>data_to_model</code> hooks <code>_populate</code> <code>get_by_id</code>/<code>get_many</code> return fully populated models.</li>
<li><code>_depopulate(document)</code> — collapses populated FK fields back to <code>ObjectId</code> before <code>create</code>/<code>update</code>.</li>
<li><code>create</code> / <code>update</code> accept a <strong>model</strong> <code>T</code> (not a dict) so depopulation can run.</li>
<li><code>patch</code> blocks FK fields — raises <code>ValueError("Cannot patch FK fields via patch(): ... Use update() to change FK fields.")</code>.</li>
</ul>
<h3 id="3-baserepositoryt">3. BaseRepository[T]</h3>
<p>Generic repository with CRUD operations:</p>
<blockquote>
<p><strong>Populate semantics (important):</strong> a <code>PopulateRule</code> names a field that holds either an <code>ObjectId</code> or a <code>list[ObjectId]</code> <strong>and is the same field that gets replaced</strong> with the resolved document(s). There is no separate "ref field" vs "target field". See <a href="../03_use_cases/08_population/">use case 08</a>.</p>
</blockquote>
<hr />
<h2 id="6-transactionmanager">6. TransactionManager</h2>
<table>
<thead>
<tr>
<th>Method</th>
<th>Signature</th>
<th>Behavior</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>start_session</code></td>
<td><code>async ctx manager (**kwargs) -&gt; AsyncIOMotorClientSession</code></td>
<td>Yields a session with an active transaction. Pass <code>session=</code> to every collection call inside.</td>
</tr>
<tr>
<td><code>execute_transaction</code></td>
<td><code>async (operations: list[Callable[[session], Awaitable[Any]]], **kwargs) -&gt; list[Any]</code></td>
<td>Runs each op inside one transaction and returns results in order; any exception aborts the transaction and propagates.</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="7-modelregistry">7. ModelRegistry</h2>
<p>Centralized models, indexes, and cache lifecycle for multi-collection services.</p>
<table>
<thead>
<tr>
<th>Method</th>
<th>Signature</th>
<th>Behavior</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>register</code></td>
<td><code>(collection_name: str, model: type[BaseDocument], indexes: list[Any] \| None = None)</code></td>
<td>Records model + index specs. Index specs are passed <strong>as-is</strong> to pymongo <code>create_index</code> — single tuples, compound lists, or dicts with <code>keys</code>/<code>options</code>.</td>
</tr>
<tr>
<td><code>initialize_all</code></td>
<td><code>async (db: AsyncIOMotorDatabase \| None = None) -&gt; None</code></td>
<td><code>create_index</code> per registered spec (idempotent). Uses the manager database if <code>db</code> omitted.</td>
</tr>
<tr>
<td><code>get_model</code></td>
<td><code>(collection_name) -&gt; type[BaseDocument]</code></td>
<td>Raises <code>KeyError</code> if unregistered.</td>
</tr>
<tr>
<td><code>list_collections</code></td>
<td><code>() -&gt; list[str]</code></td>
<td>Registered collection names.</td>
</tr>
<tr>
<td><code>set_cache_backend</code></td>
<td><code>(backend: CacheBackend) -&gt; None</code></td>
<td>Register the single shared backend.</td>
</tr>
<tr>
<td><code>initialize_cache</code></td>
<td><code>async () -&gt; None</code></td>
<td>Starts the backend (background TTL cleanup) — raises <code>RuntimeError</code> if no backend registered.</td>
</tr>
<tr>
<td><code>shutdown_cache</code></td>
<td><code>async () -&gt; None</code></td>
<td>Stops the backend cleanly and clears it.</td>
</tr>
<tr>
<td><code>get_cache_backend</code></td>
<td><code>() -&gt; Optional[CacheBackend]</code></td>
<td>Current backend, if any.</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="8-cache-layer">8. Cache Layer</h2>
<h3 id="81-cachebackend-abstract">8.1 CacheBackend (abstract)</h3>
<p>Async interface: <code>get(key) -&gt; Optional[bytes]</code>, <code>set(key, value: bytes, ttl | None)</code>, <code>delete(key)</code>, <code>exists(key)</code>, <code>clear_pattern(pattern)</code>, <code>get_stats() -&gt; CacheStats</code>, <code>initialize()</code>, <code>shutdown()</code>. Values are <strong>bytes</strong> (JSON-encoded).</p>
<h3 id="82-cachestats">8.2 CacheStats</h3>
<p>Dataclass: <code>hits</code>, <code>misses</code>, <code>sets</code>, <code>deletes</code>, <code>current_size</code>, <code>max_size</code>.</p>
<h3 id="83-cacheconfig">8.3 CacheConfig</h3>
<p>Dataclass: <code>enabled: bool = True</code>, <code>backend: Literal["memory", "redis"] = "memory"</code>, <code>redis_client</code>, <code>default_ttl: int = 300</code>, <code>max_entries: int = 10000</code>, <code>key_prefix: str = ""</code>, <code>cleanup_interval: int = 60</code>. Raises <code>ValueError</code> if <code>backend="redis"</code> without a client, and <code>ImportError</code> if <code>redis</code> is not installed.</p>
<h3 id="84-inmemorycachebackend">8.4 InMemoryCacheBackend</h3>
<p><code>InMemoryCacheBackend(max_entries=10000, default_ttl=300, cleanup_interval=60)</code> — LRU <code>OrderedDict</code> + TTL heap; <code>initialize()</code> spawns the periodic cleanup task (TTL 0 expires immediately).</p>
<h3 id="85-rediscachebackend">8.5 RedisCacheBackend</h3>
<p><code>RedisCacheBackend(redis_client, key_prefix="", default_ttl=300)</code><code>setex</code> storage, SCAN-based <code>clear_pattern</code>, and <code>publish_invalidate(key)</code> for cross-service invalidation on <code>delete</code> via the <code>mongo_ops:cache:invalidate</code> channel.</p>
<h3 id="86-cachedbaserepositoryt">8.6 CachedBaseRepository[T]</h3>
<p><code>CachedBaseRepository(collection_name, model, cache_backend: CacheBackend, config: CacheConfig | None = None)</code>.</p>
<ul>
<li><code>create(data: T) -&gt; T</code></li>
<li><code>get_by_id(id: str | ObjectId) -&gt; Optional[T]</code></li>
<li><code>get_many(filter, skip, limit, sort) -&gt; List[T]</code></li>
<li><code>update(id, data: Dict) -&gt; Optional[T]</code></li>
<li><code>delete(id) -&gt; bool</code></li>
<li><code>count(filter) -&gt; int</code></li>
</ul>
<h3 id="4-transactionmanager">4. TransactionManager</h3>
<p>Handles multi-document transactions:</p>
<ul>
<li><code>start_session()</code> - Context manager for transactions</li>
<li><code>execute_transaction(operations)</code> - Execute multiple operations atomically</li>
</ul>
<h3 id="5-modelregistry">5. ModelRegistry</h3>
<p>Register and initialize models:</p>
<ul>
<li><code>register(collection_name, model, indexes)</code> - Register a model</li>
<li><code>initialize_all()</code> - Create all indexes</li>
<li><code>get_model(collection_name)</code> - Get registered model</li>
<li><code>list_collections()</code> - List all registered collections</li>
<li>Cache keys are <code>"{key_prefix}{id}"</code> (prefix defaults to <code>"{collection_name}:"</code>).</li>
<li><code>get_by_id</code> — cache-first; cache miss reads DB and stores <code>model_dump(by_alias=True)</code> (JSON-encoded) for <code>default_ttl</code>. Honors <code>config.enabled=False</code> (bypass).</li>
<li><code>create</code> — inserts then caches the result.</li>
<li><code>update</code>/<code>delete</code> — refresh or remove the cache entry.</li>
<li><code>warm_cache(ids) -&gt; int</code> — prefetch a list of IDs, returns count warmed.</li>
<li><code>invalidate_cache(id)</code> — manual eviction.</li>
</ul>
<hr />
<h3 id="6-cache-backend">6. Cache Backend</h3>
<h4 id="61-cachebackend-abstract">6.1 CacheBackend (abstract)</h4>
<h2 id="9-population-layer">9. Population Layer</h2>
<h3 id="91-populaterule">9.1 PopulateRule</h3>
<p>Dataclass:</p>
<div class="language-python highlight"><span class="filename">Python</span><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="nd">@dataclass</span>
<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="k">class</span><span class="w"> </span><span class="nc">PopulateRule</span><span class="p">:</span>
<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a> <span class="n">field_name</span><span class="p">:</span> <span class="nb">str</span> <span class="c1"># field holding the ObjectId / list[ObjectId]; replaced in-place with the resolved doc(s)</span>
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a> <span class="n">collection_name</span><span class="p">:</span> <span class="nb">str</span> <span class="c1"># collection the references point at</span>
<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a> <span class="n">nested_rules</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">PopulateRule</span><span class="p">]</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a> <span class="n">max_depth</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">1</span>
<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a> <span class="nb">filter</span><span class="p">:</span> <span class="nb">dict</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># DECLARED but NOT yet applied by the engine</span>
<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a> <span class="n">projection</span><span class="p">:</span> <span class="nb">dict</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># DECLARED but NOT yet applied by the engine</span>
</code></pre></div>
<blockquote>
<p>⚠️ <code>filter</code> and <code>projection</code> are accepted but currently <strong>ignored</strong> by <code>PopulationEngine</code> — do not rely on them yet.</p>
</blockquote>
<h3 id="92-populationengine">9.2 PopulationEngine</h3>
<p><code>PopulationEngine(repos: dict[str, Any], global_max_depth: int = 10)</code><code>repos</code> maps <code>collection_name</code> → repository.</p>
<ul>
<li>Defines the async interface for cache operations: <code>get</code>, <code>set</code>, <code>delete</code>, <code>exists</code>, <code>clear_pattern</code>, <code>get_stats</code>, <code>initialize</code>, <code>shutdown</code>.</li>
<li>Provides <code>CacheStats</code> for hit/miss/size metrics and <code>CircularReferenceError</code> for cycle detection.</li>
</ul>
<h4 id="62-cacheconfig">6.2 CacheConfig</h4>
<ul>
<li>Dataclass to configure caching (<code>enabled</code>, <code>backend</code>, <code>redis_client</code>, <code>default_ttl</code>, <code>max_entries</code>, <code>key_prefix</code>, <code>cleanup_interval</code>).</li>
</ul>
<h4 id="63-inmemorycachebackend">6.3 InMemoryCacheBackend</h4>
<ul>
<li>Inprocess cache using an LRU <code>OrderedDict</code> and a TTL heap.</li>
<li>Background task periodically evicts expired entries.</li>
</ul>
<h4 id="64-rediscachebackend">6.4 RedisCacheBackend</h4>
<ul>
<li>Distributed cache based on <code>redis.asyncio</code>.</li>
<li>JSON serialisation, optional pub/sub invalidation.</li>
</ul>
<h4 id="65-cachedbaserepositoryt">6.5 CachedBaseRepository[T]</h4>
<ul>
<li>Extends <code>BaseRepository</code> with transparent IDbased caching.</li>
<li>Methods:</li>
<li><code>get_by_id</code> cachefirst lookup.</li>
<li><code>create</code> stores newly created doc in cache.</li>
<li><code>update</code> / <code>delete</code> invalidate or refresh cache.</li>
<li><code>warm_cache(ids)</code> preload a list of IDs.</li>
<li><code>invalidate_cache(id)</code> manual invalidation.</li>
<li><code>register_repo(collection_name, repo)</code> — add repositories at runtime.</li>
<li><code>populate(document, rules, depth=0)</code> — resolves refs recursively, replacing <code>field_name</code> in place; raises <code>CircularReferenceError(collection, doc_id, path)</code> when a <code>(Class, id)</code> pair is revisited.</li>
<li><code>depopulate(document, rules)</code> — collapse populated docs back to IDs (for storage).</li>
<li><code>global_max_depth</code> caps recursion; per-rule <code>max_depth</code> bounds a rule's descent.</li>
</ul>
<h3 id="93-circularreferenceerrorvalueerror">9.3 CircularReferenceError(ValueError)</h3>
<p>Holds <code>collection</code>, <code>doc_id</code>, and the visited <code>path</code> for debugging cycle messages.</p>
<hr />
<h3 id="7-population-engine">7. Population Engine</h3>
<h4 id="71-populaterule">7.1 PopulateRule</h4>
<h2 id="related">Related</h2>
<ul>
<li>Dataclass defining a population rule: <code>field_name</code>, <code>collection_name</code>, <code>ref_field</code>, optional <code>nested_rules</code>, <code>max_depth</code>, <code>filter</code>, <code>projection</code>.</li>
<li><a href="../03_use_cases/01_basic_crud/">Use case 01 Basic CRUD</a> · <a href="../03_use_cases/07_caching/">07 Caching</a> · <a href="../03_use_cases/08_population/">08 Population</a></li>
</ul>
<h4 id="72-populationengine">7.2 PopulationEngine</h4>
<ul>
<li>Recursively resolves references according to <code>PopulateRule</code> list.</li>
<li>Detects circular references and raises <code>CircularReferenceError</code>.</li>
<li>Supports perrule depth limits, filters and projections.</li>
</ul>
<h4 id="73-populatingrepositoryt">7.3 PopulatingRepository[T]</h4>
<ul>
<li>Extends <code>BaseRepository</code> to automatically populate related documents.</li>
<li>Accepts an optional <code>PopulationEngine</code> and a list of <code>PopulateRule</code>.</li>
<li>Provides <code>_populate</code> and <code>_depopulate</code> helpers used in <code>get_by_id</code> and elsewhere.</li>
</ul>
<hr />