Files
obikmer/DevDoc/implementation/obitaxonomy/index.html
T
Eric Coissac 5c1584967f feat: enable index resumption and enforce directory creation
The command now supports reopening existing indexes instead of failing when the output file exists. Control flow branches between opening an existing index and constructing a new one, moving configuration setup exclusively to the creation path. Directory existence is enforced upfront with proper I/O error propagation. The --force flag retains its original semantics by removing the target directory before proceeding with a fresh build.
2026-08-21 05:06:38 +02:00

1800 lines
36 KiB
HTML

<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="prev" href="../select/">
<link rel="next" href="../benchmark_query_testing/">
<link rel="icon" href="../../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.6">
<title>obitaxonomy crate - obikmer</title>
<link rel="stylesheet" href="../../assets/stylesheets/main.484c7ddc.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
<script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
</head>
<body dir="ltr">
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer"></label>
<div data-md-component="skip">
<a href="#obitaxonomy-taxonomy-concept-paths" class="md-skip">
Skip to content
</a>
</div>
<div data-md-component="announce">
</div>
<header class="md-header md-header--shadow" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Header">
<a href="../.." title="obikmer" class="md-header__button md-logo" aria-label="obikmer" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
</a>
<label class="md-header__button md-icon" for="__drawer">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
obikmer
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
obitaxonomy crate
</span>
</div>
</div>
</div>
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
</nav>
</header>
<div class="md-container" data-md-component="container">
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href="../.." title="obikmer" class="md-nav__button md-logo" aria-label="obikmer" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
</a>
obikmer
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../.." class="md-nav__link">
<span class="md-ellipsis">
Home
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../installation/" class="md-nav__link">
<span class="md-ellipsis">
Installation
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_3" >
<label class="md-nav__link" for="__nav_3" id="__nav_3_label" tabindex="0">
<span class="md-ellipsis">
Theory
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_3_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_3">
<span class="md-nav__icon md-icon"></span>
Theory
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../kmers/" class="md-nav__link">
<span class="md-ellipsis">
Kmers and super-kmers
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../theory/encoding/" class="md-nav__link">
<span class="md-ellipsis">
DNA encoding
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../theory/entropy/" class="md-nav__link">
<span class="md-ellipsis">
Entropy filter
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../theory/minimizer/" class="md-nav__link">
<span class="md-ellipsis">
Minimizer selection
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../theory/indexing/" class="md-nav__link">
<span class="md-ellipsis">
Partitioning architecture
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../theory/evolutionary_distances/" class="md-nav__link">
<span class="md-ellipsis">
Central-position SNP distance (discussion)
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4" checked>
<label class="md-nav__link" for="__nav_4" id="__nav_4_label" tabindex="0">
<span class="md-ellipsis">
Implementation
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_4_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_4">
<span class="md-nav__icon md-icon"></span>
Implementation
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../superkmer/" class="md-nav__link">
<span class="md-ellipsis">
SuperKmer
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../kmer/" class="md-nav__link">
<span class="md-ellipsis">
Kmer
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../chunkreader/" class="md-nav__link">
<span class="md-ellipsis">
Chunk reader
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../pipeline/" class="md-nav__link">
<span class="md-ellipsis">
Construction pipeline
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../obipipeline/" class="md-nav__link">
<span class="md-ellipsis">
obipipeline library
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../storage/" class="md-nav__link">
<span class="md-ellipsis">
On-disk storage
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../mphf/" class="md-nav__link">
<span class="md-ellipsis">
MPHF selection
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../unitig_evidence/" class="md-nav__link">
<span class="md-ellipsis">
Unitig evidence encoding
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../evidence_elimination/" class="md-nav__link">
<span class="md-ellipsis">
Evidence elimination (discussion)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../layer_tier/" class="md-nav__link">
<span class="md-ellipsis">
obikindex layer tier
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../persistent_compact_int_vec/" class="md-nav__link">
<span class="md-ellipsis">
PersistentCompactIntVec
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../persistent_bit_vec/" class="md-nav__link">
<span class="md-ellipsis">
PersistentBitVec
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../merge/" class="md-nav__link">
<span class="md-ellipsis">
Merge command
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../merge_parallelism/" class="md-nav__link">
<span class="md-ellipsis">
Merge parallelism & memory
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../filtering/" class="md-nav__link">
<span class="md-ellipsis">
Kmer filtering
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../select/" class="md-nav__link">
<span class="md-ellipsis">
Select command
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--active">
<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">
obitaxonomy crate
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<a href="./" class="md-nav__link md-nav__link--active">
<span class="md-ellipsis">
obitaxonomy crate
</span>
</a>
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Table of contents
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#concept-path-syntax" class="md-nav__link">
<span class="md-ellipsis">
Concept path syntax
</span>
</a>
<nav class="md-nav" aria-label="Concept path syntax">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#reserved-character" class="md-nav__link">
<span class="md-ellipsis">
Reserved character
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#parse-errors" class="md-nav__link">
<span class="md-ellipsis">
Parse errors
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#public-api" class="md-nav__link">
<span class="md-ellipsis">
Public API
</span>
</a>
<nav class="md-nav" aria-label="Public API">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#taxsegment" class="md-nav__link">
<span class="md-ellipsis">
TaxSegment
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#taxpath" class="md-nav__link">
<span class="md-ellipsis">
TaxPath
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#integration-with-genomeinfo" class="md-nav__link">
<span class="md-ellipsis">
Integration with GenomeInfo
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#predicate-operators-in-filter-select" class="md-nav__link">
<span class="md-ellipsis">
Predicate operators (in filter / select)
</span>
</a>
<nav class="md-nav" aria-label="Predicate operators (in filter / select)">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#path-pattern-syntax" class="md-nav__link">
<span class="md-ellipsis">
Path pattern syntax
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#rank-aware-queries" class="md-nav__link">
<span class="md-ellipsis">
Rank-aware queries
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../benchmark_query_testing/" class="md-nav__link">
<span class="md-ellipsis">
Benchmark: query-path testing
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../partition_layer_cache/" class="md-nav__link">
<span class="md-ellipsis">
Partition and layer caching (discussion)
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5" >
<label class="md-nav__link" for="__nav_5" id="__nav_5_label" tabindex="0">
<span class="md-ellipsis">
Architecture
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_5_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_5">
<span class="md-nav__icon md-icon"></span>
Architecture
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../architecture/sequences/invariant/" class="md-nav__link">
<span class="md-ellipsis">
Sequences
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../architecture/index_architecture/" class="md-nav__link">
<span class="md-ellipsis">
Kmer index
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../architecture/siblings/" class="md-nav__link">
<span class="md-ellipsis">
Sibling annex (discussion)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../architecture/numa_worker_pools/" class="md-nav__link">
<span class="md-ellipsis">
NUMA-aware worker pools
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../architecture/numa_partition_runner/" class="md-nav__link">
<span class="md-ellipsis">
NUMA-aware partition runner
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Table of contents
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#concept-path-syntax" class="md-nav__link">
<span class="md-ellipsis">
Concept path syntax
</span>
</a>
<nav class="md-nav" aria-label="Concept path syntax">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#reserved-character" class="md-nav__link">
<span class="md-ellipsis">
Reserved character
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#parse-errors" class="md-nav__link">
<span class="md-ellipsis">
Parse errors
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#public-api" class="md-nav__link">
<span class="md-ellipsis">
Public API
</span>
</a>
<nav class="md-nav" aria-label="Public API">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#taxsegment" class="md-nav__link">
<span class="md-ellipsis">
TaxSegment
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#taxpath" class="md-nav__link">
<span class="md-ellipsis">
TaxPath
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#integration-with-genomeinfo" class="md-nav__link">
<span class="md-ellipsis">
Integration with GenomeInfo
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#predicate-operators-in-filter-select" class="md-nav__link">
<span class="md-ellipsis">
Predicate operators (in filter / select)
</span>
</a>
<nav class="md-nav" aria-label="Predicate operators (in filter / select)">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#path-pattern-syntax" class="md-nav__link">
<span class="md-ellipsis">
Path pattern syntax
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#rank-aware-queries" class="md-nav__link">
<span class="md-ellipsis">
Rank-aware queries
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<article class="md-content__inner md-typeset">
<h1 id="obitaxonomy-taxonomy-concept-paths"><code>obitaxonomy</code> — taxonomy concept paths</h1>
<p><code>obitaxonomy</code> is a dependency-free crate that defines a typed representation
of hierarchical concept paths (taxonomic or otherwise) stored in genome metadata.</p>
<hr />
<h2 id="concept-path-syntax">Concept path syntax</h2>
<p>A concept path is stored as a metadata value with the prefix <code>taxonomy:/</code>:</p>
<div class="highlight"><pre><span></span><code>taxonomy:/enterobacteriaceae@family/Escherichia@genus/Escherichia coli@species
</code></pre></div>
<p>Structure:</p>
<ul>
<li>The <code>taxonomy:/</code> prefix is the type discriminator. Any metadata value starting
with it is parsed as a <code>TaxPath</code>; all others remain plain strings.</li>
<li>The remainder is one or more <code>/</code>-separated segments.</li>
<li>Each segment is <code>name</code> or <code>name@rank</code>, where <code>rank</code> is a label for the
taxonomic level (e.g. <code>family</code>, <code>genus</code>, <code>species</code>).</li>
<li>Rank annotations are <strong>optional per segment</strong> and can be mixed freely.</li>
<li>Spaces are allowed in both names and ranks.</li>
</ul>
<h3 id="reserved-character">Reserved character</h3>
<p><code>@</code> is reserved throughout the taxonomy system and may <strong>not</strong> appear in:</p>
<table>
<thead>
<tr>
<th>Context</th>
<th>Constraint</th>
</tr>
</thead>
<tbody>
<tr>
<td>Segment name</td>
<td>forbidden</td>
</tr>
<tr>
<td>Rank/class label</td>
<td>forbidden</td>
</tr>
<tr>
<td>Metadata key names</td>
<td>forbidden (used as <code>key@rank</code> in predicate syntax)</td>
</tr>
</tbody>
</table>
<p><code>@</code> is freely allowed in plain-text metadata values (non-taxonomy).</p>
<h3 id="parse-errors">Parse errors</h3>
<table>
<thead>
<tr>
<th>Condition</th>
<th>Error</th>
</tr>
</thead>
<tbody>
<tr>
<td>Value does not start with <code>taxonomy:/</code></td>
<td><code>MissingPrefix</code></td>
</tr>
<tr>
<td>No segments after the prefix</td>
<td><code>EmptyPath</code></td>
</tr>
<tr>
<td>Segment with empty name (consecutive <code>/</code>)</td>
<td><code>EmptySegmentName</code></td>
</tr>
<tr>
<td>Segment with trailing <code>@</code> and no rank (<code>name@</code>)</td>
<td><code>EmptyRankName</code></td>
</tr>
<tr>
<td>Segment with more than one <code>@</code></td>
<td><code>AmbiguousRank</code></td>
</tr>
</tbody>
</table>
<hr />
<h2 id="public-api">Public API</h2>
<h3 id="taxsegment"><code>TaxSegment</code></h3>
<p>A single node: a name and an optional rank.</p>
<div class="highlight"><pre><span></span><code><span class="n">seg</span><span class="p">.</span><span class="n">name</span><span class="p">()</span><span class="w"> </span><span class="c1">// &amp;str</span>
<span class="n">seg</span><span class="p">.</span><span class="n">rank</span><span class="p">()</span><span class="w"> </span><span class="c1">// Option&lt;&amp;str&gt;</span>
<span class="n">seg</span><span class="p">.</span><span class="n">to_string</span><span class="p">()</span><span class="w"> </span><span class="c1">// &quot;name&quot; or &quot;name@rank&quot;</span>
<span class="n">TaxSegment</span><span class="p">::</span><span class="n">parse</span><span class="p">(</span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="c1">// Result&lt;TaxSegment, TaxError&gt;</span>
</code></pre></div>
<h3 id="taxpath"><code>TaxPath</code></h3>
<div class="highlight"><pre><span></span><code><span class="n">TaxPath</span><span class="p">::</span><span class="n">parse</span><span class="p">(</span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="c1">// Result&lt;TaxPath, TaxError&gt;</span>
<span class="n">path</span><span class="p">.</span><span class="n">segments</span><span class="p">()</span><span class="w"> </span><span class="c1">// &amp;[TaxSegment]</span>
<span class="n">path</span><span class="p">.</span><span class="n">depth</span><span class="p">()</span><span class="w"> </span><span class="c1">// usize — number of segments</span>
<span class="n">path</span><span class="p">.</span><span class="n">is_ancestor_of</span><span class="p">(</span><span class="o">&amp;</span><span class="n">other</span><span class="p">)</span><span class="w"> </span><span class="c1">// bool — prefix match by name, ranks ignored</span>
<span class="n">path</span><span class="p">.</span><span class="n">name_at_rank</span><span class="p">(</span><span class="s">&quot;genus&quot;</span><span class="p">)</span><span class="w"> </span><span class="c1">// Option&lt;&amp;str&gt;</span>
<span class="n">path</span><span class="p">.</span><span class="n">to_string</span><span class="p">()</span><span class="w"> </span><span class="c1">// reconstructs &quot;taxonomy:/…&quot;</span>
</code></pre></div>
<p><code>is_ancestor_of</code> compares segment <strong>names</strong> only — rank annotations are
informational and do not affect the ancestry relation.</p>
<div class="highlight"><pre><span></span><code><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="p">:</span><span class="w"> </span><span class="nc">TaxPath</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&quot;taxonomy:/Enterobacteriaceae@family/Escherichia@genus&quot;</span><span class="p">.</span><span class="n">parse</span><span class="p">()</span><span class="o">?</span><span class="p">;</span>
<span class="kd">let</span><span class="w"> </span><span class="n">b</span><span class="p">:</span><span class="w"> </span><span class="nc">TaxPath</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&quot;taxonomy:/Enterobacteriaceae@family/Escherichia@genus/Escherichia coli@species&quot;</span><span class="p">.</span><span class="n">parse</span><span class="p">()</span><span class="o">?</span><span class="p">;</span>
<span class="fm">assert!</span><span class="p">(</span><span class="n">a</span><span class="p">.</span><span class="n">is_ancestor_of</span><span class="p">(</span><span class="o">&amp;</span><span class="n">b</span><span class="p">));</span><span class="w"> </span><span class="c1">// true</span>
<span class="fm">assert!</span><span class="p">(</span><span class="n">b</span><span class="p">.</span><span class="n">is_ancestor_of</span><span class="p">(</span><span class="o">&amp;</span><span class="n">a</span><span class="p">));</span><span class="w"> </span><span class="c1">// false</span>
<span class="fm">assert!</span><span class="p">(</span><span class="n">a</span><span class="p">.</span><span class="n">is_ancestor_of</span><span class="p">(</span><span class="o">&amp;</span><span class="n">a</span><span class="p">));</span><span class="w"> </span><span class="c1">// true (equal ⇒ ancestor)</span>
<span class="fm">assert_eq!</span><span class="p">(</span><span class="n">b</span><span class="p">.</span><span class="n">name_at_rank</span><span class="p">(</span><span class="s">&quot;species&quot;</span><span class="p">),</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="s">&quot;Escherichia coli&quot;</span><span class="p">));</span>
<span class="fm">assert_eq!</span><span class="p">(</span><span class="n">b</span><span class="p">.</span><span class="n">name_at_rank</span><span class="p">(</span><span class="s">&quot;genus&quot;</span><span class="p">),</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="s">&quot;Escherichia&quot;</span><span class="p">));</span>
<span class="fm">assert_eq!</span><span class="p">(</span><span class="n">b</span><span class="p">.</span><span class="n">name_at_rank</span><span class="p">(</span><span class="s">&quot;order&quot;</span><span class="p">),</span><span class="w"> </span><span class="nb">None</span><span class="p">);</span>
</code></pre></div>
<hr />
<h2 id="integration-with-genomeinfo">Integration with <code>GenomeInfo</code></h2>
<p>At index load time, every metadata value is inspected once:</p>
<ul>
<li>Starts with <code>taxonomy:/</code> → parsed into <code>TaxPath</code>, stored in <code>genome.taxonomy</code>.</li>
<li>Otherwise → kept as-is in <code>genome.meta</code>.</li>
</ul>
<div class="highlight"><pre><span></span><code><span class="k">struct</span><span class="w"> </span><span class="nc">GenomeInfo</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">label</span><span class="p">:</span><span class="w"> </span><span class="nb">String</span><span class="p">,</span>
<span class="w"> </span><span class="n">meta</span><span class="p">:</span><span class="w"> </span><span class="nc">HashMap</span><span class="o">&lt;</span><span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="nb">String</span><span class="o">&gt;</span><span class="p">,</span><span class="w"> </span><span class="c1">// plain text metadata</span>
<span class="w"> </span><span class="n">taxonomy</span><span class="p">:</span><span class="w"> </span><span class="nc">HashMap</span><span class="o">&lt;</span><span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="n">TaxPath</span><span class="o">&gt;</span><span class="p">,</span><span class="w"> </span><span class="c1">// parsed taxonomy metadata</span>
<span class="p">}</span>
</code></pre></div>
<p>The raw string is not duplicated. <code>TaxPath::to_string()</code> reconstructs the
original value losslessly for serialisation.</p>
<hr />
<h2 id="predicate-operators-in-filter-select">Predicate operators (in <code>filter</code> / <code>select</code>)</h2>
<p>Path predicates use the <code>~</code> / <code>!~</code> operators. The <strong>stored value</strong> always starts
with <code>/</code> (rooted path); the <strong>query pattern</strong> does not need to.</p>
<h3 id="path-pattern-syntax">Path pattern syntax</h3>
<table>
<thead>
<tr>
<th>Pattern</th>
<th>Semantics</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>A/B</code></td>
<td>contiguous sub-path A then B, anywhere in the value</td>
</tr>
<tr>
<td><code>/A/B</code></td>
<td>value starts with A then B (start-anchored)</td>
</tr>
<tr>
<td><code>A/B$</code></td>
<td>value ends with A then B (end-anchored)</td>
</tr>
<tr>
<td><code>/A/B$</code></td>
<td>value is exactly A then B (fully anchored)</td>
</tr>
<tr>
<td><code>A@x/B</code></td>
<td>A with class <code>x</code> followed by B with any class</td>
</tr>
<tr>
<td><code>A@x/B@y</code></td>
<td>A with class <code>x</code> followed by B with class <code>y</code></td>
</tr>
</tbody>
</table>
<p>A segment pattern without <code>@</code> matches the segment name regardless of its stored class.</p>
<h3 id="rank-aware-queries">Rank-aware queries</h3>
<div class="highlight"><pre><span></span><code>key@rank=value
</code></pre></div>
<table>
<thead>
<tr>
<th>Predicate form</th>
<th>Semantics</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>key@rank=value</code></td>
<td>genome's <code>key</code> has <code>value</code> at rank <code>rank</code></td>
</tr>
<tr>
<td><code>key@rank!=value</code></td>
<td>does not</td>
</tr>
<tr>
<td><code>key@rank=v1\|v2</code></td>
<td>value at <code>rank</code> is <code>v1</code> or <code>v2</code></td>
</tr>
</tbody>
</table>
<p><code>~</code> combined with <code>@rank</code> on the key (e.g. <code>key@genus~pattern</code>) is not defined
and is rejected at parse time.</p>
</article>
</div>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</div>
</main>
<footer class="md-footer">
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-copyright">
Made with
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
Material for MkDocs
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<script id="__config" type="application/json">{"annotate": null, "base": "../..", "features": [], "search": "../../assets/javascripts/workers/search.2c215733.min.js", "tags": null, "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": null}</script>
<script src="../../assets/javascripts/bundle.79ae519e.min.js"></script>
<script src="https://unpkg.com/mathjax@3/es5/tex-mml-chtml.js"></script>
</body>
</html>