# Search

> **Owned by rmfmail. Do not edit this file** — it's reference documentation.

Search in rmfmail runs in one of three modes depending on what you've selected:

- **All folders** (default) — searches your **local cache** using SQLite FTS5
- **This folder** — instant client-side filter on visible rows; full FTS5 search on Enter
- **Server** (the "Server" checkbox) — sends the query to the mail server (IMAP SEARCH on bobma-style accounts; **not yet wired to Gmail API for Gmail accounts** — see "Limitations" below)

The mode you're in dictates which query operators work. Scope matters.

## Common shortcuts (work in every mode)

These are parsed by mailx before dispatch, so they apply to all three scopes:

| Form | Effect |
|---|---|
| `from:bob` | Match sender substring |
| `to:eleanor` | Match recipient substring |
| `subject:lunch` | Match subject substring (everything until the next `keyword:` or end) |
| `/regex/` | Client-side regex over the currently-visible rows. **Case-insensitive.** Local only — never sent to the server. |

The remaining (unqualified) text becomes the **body** search term in the server query, or a free-text FTS5 phrase in local mode.

## Mode 1: Local search (FTS5) — default

Uses SQLite's FTS5 full-text index built from envelopes + locally-cached bodies. Fast, indexed, ~1ms per query.

Supported FTS5 syntax (when the search box has more than one word and no scope checkbox):

| Operator | Example | Meaning |
|---|---|---|
| `term1 term2` | `bob lunch` | both must appear (implicit AND) |
| `term1 OR term2` | `bob OR eleanor` | either |
| `term1 NOT term2` | `bob NOT spam` | first without the second |
| `"phrase"` | `"happy birthday"` | exact phrase |
| `term*` | `lunch*` | prefix |
| `^col:term` | `^subject:budget` | restrict to one indexed column (subject, body, from, to) |
| `NEAR(a b, 5)` | `NEAR(bob lunch, 5)` | both within 5 tokens of each other |

**Case**: FTS5 is **case-insensitive**. `AND`/`OR`/`NOT` work in any case (`and`, `OR`, `Not` are equivalent).

What's actually indexed: subject, from, to, cc, snippet (first ~200 chars of body), and the full body text *if* the body has been downloaded locally (the blue dot in the list). Bodies that haven't been prefetched aren't searchable until they are.

## Mode 2: This-folder filter

When the scope dropdown is set to "This folder" and you type, mailx **client-side filters the currently rendered rows** instantly — no server hit, no FTS5. Just substring match across each visible row's text.

Press **Enter** to escalate to a full FTS5 search of the folder.

`/regex/` works here for fast on-screen filtering.

## Mode 3: Server search

Tick the "Server" checkbox and the query goes to the mail server.

### bobma / Dovecot (IMAP SEARCH)

Standard IMAP keys, server-side. Combinable with implicit AND. mailx parses your `from:`, `to:`, `subject:` qualifiers and forwards them as IMAP keys; remaining text becomes a `BODY` search.

| Key | Example | Meaning |
|---|---|---|
| `BODY <text>` | (default for unqualified words) | Substring of body |
| `TEXT <text>` | (not exposed in the UI yet) | Substring of header + body |
| `SUBJECT <text>` | `subject:budget` | Substring of subject |
| `FROM <addr>` | `from:bob` | Substring of From line |
| `TO <addr>` | `to:eleanor` | Substring of To line |
| `SINCE <date>` | (not exposed) | RFC 822 date |

**Case**: IMAP SEARCH is **case-insensitive** (per RFC 3501 §6.4.4). `bob AND eleanor` and `bob and eleanor` behave the same on Dovecot.

**`AND` / `OR`**: IMAP's grammar is implicit-AND between keys. There's no literal `AND` keyword — typing `bob AND eleanor` would be sent as a body search for the literal three-word string. To OR keys you'd use IMAP's `OR <key1> <key2>` prefix syntax, which mailx doesn't expose in the UI today.

So if you typed `bob AND eleanor` on bobma and got results, what likely happened is the body literally contained the string "bob and eleanor" (case-insensitive). Apparent "AND" success is coincidence with the literal text.

### Gmail (server-search currently unwired)

`searchAndFetchOnServer` is IMAP-only and `withConnection` doesn't exist for Gmail-API accounts. Server-scope queries on Gmail today **return nothing new** — they only hit local cache. This is a known gap; the fix is to add a Gmail-API path that uses Google's `q=` query parameter (full Gmail search syntax: `from:`, `subject:`, `has:attachment`, `older_than:1y`, `label:`, etc., with proper boolean `AND`/`OR`).

That explains the asymmetry you may have noticed: `bob AND eleanor` "worked" against bobma (literal-text coincidence) but not against Gmail (no IMAP/API call ever fired).

## Case sensitivity

All three modes are **case-insensitive** — local FTS5, IMAP server SEARCH, and `/regex/` filtering alike. `Lunch`, `lunch`, and `LUNCH` match the same messages. There's no way to force a case-sensitive search.

## Limitations

- **No regex on the server side** (any provider). `/pattern/` only filters the visible local rows.
- **Server search results are stored locally** (envelope-only) on first hit. So a server search is also a backfill of envelopes for those messages — they appear next to local results without re-fetching.
- **Body search on bodies you haven't prefetched** only works server-side. The local FTS5 index can't search what isn't downloaded.

## Practical patterns

| Goal | What to type | Scope |
|---|---|---|
| Quick keyword in current folder | `lunch` | This folder (instant) |
| All-time mail from someone | `from:bob.frankston@example.com` | All folders (FTS5) |
| Mail mentioning two people | `bob eleanor` | All folders (FTS5; implicit AND) |
| Either of two terms | `bob OR eleanor` | All folders (FTS5) |
| Exact phrase | `"open source"` | All folders (FTS5) |
| Regex over what's on screen | `/^Re: \[mailx\]/` | Any (client-side) |
| Old mail beyond `historyDays` | type query, then check the **Server** checkbox | Server (Dovecot only today) |
