# UID Lookup

> Navigate to a record by its persistent UID

---

A **UID** (`_UID` in GEDCOM 5.5.1, `UID` in GEDCOM 7.0) is a persistent identifier assigned to a record. The UID survives XREF renumbering and tree migrations, making it a stable reference point.

This feature allows navigating directly to a record by its UID. The URL pattern is:

- `/tree/{tree}/goto-uid/{uid}` — scoped to one tree
- `/goto-uid/{uid}` — global (searches all trees)

**Behavior:**

- **0 hits** → 404
- **1 visible hit** → redirect to the record
- **>1 visible hit** → selection list (case-variant UIDs or the same UID in multiple records)

UID comparison is **case-insensitive** (GEDCOM specs do not define UID as case-sensitive). The stored value is verbatim; matching is done case-insensitively so that case-variant UIDs return a selection list rather than a possibly-wrong single redirect.

You can look up UIDs via the **quick search** input. For shorter UIDs that can be regarded as XREFs, the prefix `uid:` can be added to the search value.

### Standard navigation (`goto-id`)

`goto-id` is the standard navigation target for **every** linkenhancer link — both untyped (`wt=@REF@`) and typed (`wt=<type>@REF@`) — and resolves a reference that is either a record **XREF** or a **UID**. The URL pattern is:

- `/tree/{tree}/goto-id/{id}` — scoped to one tree (falls back to a global lookup when nothing is found in the tree, with a flash notice)
- `/goto-id/{id}` — global (searches all trees)

**Behavior** (shared `IdResolver`): the id is tried length-aware and bidirectionally — a long id (≥ 18 chars) as a UID first, a short one as an XREF first — and falls back to the other when the first yields nothing, so a resolution is never lost. 0 hits → 404, 1 visible hit → redirect, >1 → selection list.




<pre class="mermaid">sequenceDiagram
    autonumber
    participant U as 👤 User (Browser)
    participant JS as LE JavaScript
    participant WT as webtrees Server
    participant IR as IdResolver
    participant DB as Database

    Note over U,JS: Click on an LE link
    U-&gt;&gt;JS: Click on #@wt=@I234@
    JS-&gt;&gt;JS: detect href &#34;#@…&#34;
    JS-&gt;&gt;WT: GET /tree/5/goto-id/I234
    WT-&gt;&gt;IR: resolve(&#34;I234&#34;, tree=5)

    alt ID short (&lt; 18 chars) → try as XREF first
        IR-&gt;&gt;DB: SELECT … WHERE xref = &#39;I234&#39;
        DB--&gt;&gt;IR: Match?
    else ID long (&gt;= 18 chars) → try as UID first
        IR-&gt;&gt;DB: SELECT … FROM le_uid_index WHERE uid = &#39;…&#39;
        DB--&gt;&gt;IR: Match?
    end

    alt ✅ 1 match
        IR--&gt;&gt;WT: Record
        WT--&gt;&gt;U: Redirect to record page
    else 0 matches → Fallback
        IR-&gt;&gt;IR: try the other direction (XREF ⇄ UID)
    else &gt;1 matches
        WT--&gt;&gt;U: selection list
    end

    Note over IR,DB: Bidirectional — XREF ⇄ UID&lt;br/&gt;a lookup is never lost
</pre>

A typed `wt=<type>@REF@` link is a **legacy** form: the type letter is no longer used for routing (all links go through `goto-id`) and is only documentary — it still drives the diagram link for individuals and the type-mismatch hint in the cross-reference overview. New links should use the untyped `wt=@REF@` form.

### XREF route (`goto-xref`)

There is also the route `/tree/{tree}/goto-xref/{xref}` available, which allows you to navigate to the corresponding record in the tree, regardless of the record type.

### Cross-reference overview: UID targets in other trees

In the admin *Cross-Reference Overview* a link whose UID target is not found in the record's own tree is normally shown as *"target not found"*. When the UID feature is enabled, such a target is additionally resolved **globally** (across the other trees); if matches exist it is rendered as **not unique** (`?`) with the match count instead: up to 3 matches are listed as direct cross-tree links, more than 3 collapse into a single `goto-id` link (title = the UID). This behavior is hard-gated on the UID feature preference and never applies to targets that name an explicit tree.

### Index

The UID index is stored in the `le_uid_index` table (one row per UID tag occurrence, distinguished by `tag_path`). It is maintained exclusively by <a href="https://codeberg.org/bschwede/linkenhancer/src/branch/main/cli/build-uid-index.php" target="_blank" rel="noopener"><code>build-uid-index.php</code></a>
 (no live scan on request).

- **Initial build** (first run, or `--rebuild`): full scan of all GEDCOM tables, extracting all UID tags via `UidTagCollector`.
- **Incremental runs**: only records whose MD5 fingerprint changed (or were added/removed) are re-scanned.

Cron example (every 10 minutes, up to 5000 records per run):

```
*/10 * * * * cd /path/to/webtrees && php modules_v4/linkenhancer/cli/build-uid-index.php --limit=5000 >> /var/log/linkenhancer-uid-index.log 2>&1
```

The feature can be toggled on/off in the module admin page ("Activate Support" → [5] UID lookup). The current index status (row count, last build time) is shown there.

While the feature is switched off, index builds are skipped automatically (the cron run exits 0 without doing work). `--force` builds the index anyway (e.g. to prepare for re-enabling), and `--flush` works regardless of the switch.

> **Note:** UID tags at any nesting level are indexed (e.g. `0 @I1@ INDI`, `1 _UID`, `1 FAMF`, `2 _UID`). The tag path is stored per row so duplicates are distinguishable in the selection list.
