Files
jellytau/api/jellytau_lib/storage/schema/constant.MIGRATION_027.html
T

57 lines
6.6 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="One canonical “which container lists this item” link, plus an index that serves a listing in display order."><title>MIGRATION_027 in jellytau_lib::storage::schema - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../../static.files/rustdoc-17e0aaed.css"><meta name="rustdoc-vars" data-root-path="../../../" data-static-root-path="../../../static.files/" data-current-crate="jellytau_lib" data-themes="" data-resource-suffix="" data-rustdoc-version="1.97.1 (8bab26f4f 2026-07-14)" data-channel="1.97.1" data-search-js="search-fd9372ac.js" data-stringdex-js="stringdex-2da4960a.js" data-settings-js="settings-170eb4bf.js" ><script src="../../../static.files/storage-41dd4d93.js"></script><script defer src="sidebar-items.js"></script><script defer src="../../../static.files/main-fcd733ba.js"></script><noscript><link rel="stylesheet" href="../../../static.files/noscript-f7c3ffd8.css"></noscript><link rel="alternate icon" type="image/png" href="../../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../../static.files/favicon-044be391.svg"></head><body class="rustdoc constant"><a class="skip-main-content" href="#main-content">Skip to main content</a><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">MIGRATION_027</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../../../jellytau_lib/index.html">jellytau_<wbr>lib</a><span class="version">0.13.3</span></h2></div><div class="sidebar-elems"><div id="rustdoc-modnav"><h2><a href="index.html">In jellytau_<wbr>lib::<wbr>storage::<wbr>schema</a></h2></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content" tabindex="-1"><div class="main-heading"><div class="rustdoc-breadcrumbs"><a href="../../index.html">jellytau_lib</a>::<wbr><a href="../index.html">storage</a>::<wbr><a href="index.html">schema</a></div><h1>Constant <span class="constant">MIGRATION_<wbr>027</span>&nbsp;<button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../../../src/jellytau_lib/storage/schema.rs.html#974-1005">Source</a> </span></div><pre class="rust item-decl"><code>const MIGRATION_027: &amp;<a class="primitive" href="https://doc.rust-lang.org/1.97.1/std/primitive.str.html">str</a> = r#&quot;
ALTER TABLE items ADD COLUMN container_id TEXT GENERATED ALWAYS AS (
CASE item_type
WHEN &#39;Episode&#39; THEN COALESCE(season_id, series_id, parent_id)
WHEN &#39;Season&#39; THEN COALESCE(series_id, parent_id)
WHEN &#39;Audio&#39; THEN COALESCE(album_id, parent_id)
ELSE parent_id
END
) VIRTUAL;
CREATE INDEX IF NOT EXISTS idx_items_container ON items(container_id, sort_name, name);
INSERT OR IGNORE INTO items (id, server_id, library_id, name, item_type, is_folder, series_id, series_name)
SELECT season_id, server_id, MAX(library_id), COALESCE(MAX(season_name), &#39;Season&#39;), &#39;Season&#39;, 1,
MAX(series_id), MAX(series_name)
FROM items
WHERE item_type = &#39;Episode&#39; AND season_id IS NOT NULL
GROUP BY season_id;
INSERT OR IGNORE INTO items (id, server_id, library_id, name, item_type, is_folder)
SELECT series_id, server_id, MAX(library_id), COALESCE(MAX(series_name), &#39;Series&#39;), &#39;Series&#39;, 1
FROM items
WHERE item_type IN (&#39;Episode&#39;, &#39;Season&#39;) AND series_id IS NOT NULL
GROUP BY series_id;
INSERT OR IGNORE INTO items (id, server_id, library_id, name, item_type, is_folder, album_artist)
SELECT album_id, server_id, MAX(library_id), COALESCE(MAX(album_name), &#39;Album&#39;), &#39;MusicAlbum&#39;, 1,
MAX(album_artist)
FROM items
WHERE item_type = &#39;Audio&#39; AND album_id IS NOT NULL
GROUP BY album_id;
&quot;#;</code></pre><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>One canonical “which container lists this item” link, plus an index that
serves a listing in display order.</p>
<p>Jellyfins <code>ParentId</code> is the <em>storage</em> parent, not the logical one: in a
series without season folders an episodes <code>ParentId</code> is the series while
its <code>SeasonId</code> names a virtual season, and a cached episode may arrive
without its season row at all. So listings matched children on four
columns at once (<code>parent_id</code>, <code>album_id</code>, <code>season_id</code>, <code>series_id</code>). That
was slow — the <code>OR</code> defeated the planner into walking the whole table — and
wrong: every episode carries its series id, so a series answered with its
seasons <em>and</em> all their episodes.</p>
<p><code>container_id</code> resolves the logical container once, by rule: an episode
belongs to its season (else its series, else its parent), a season to its
series, a track to its album, anything else to its parent. It is a VIRTUAL
generated column, so every write path — cache, downloads, catalog crawl —
is covered without touching any of them, and it cannot drift from the
columns it is computed from. The index covers the listings
<code>ORDER BY sort_name, name</code> (<code>sort_name</code> is usually NULL in the cache).</p>
<p>The placeholders keep offline navigation intact: an episode whose season
or series row was never cached used to surface directly under the series
through the <code>series_id</code> match. Now it lists under its season, so the season
(and series, and a tracks album) must exist. They are built from the
names the child rows already carry, with <code>synced_at</code> NULL — they only show
when a download makes them available, and a real row from the server
replaces them wholesale (<code>save_to_cache</code> upserts every field).</p>
<p>TRACES: UR-002, UR-007 | DR-013</p>
</div></details></section></div></main></body></html>