# DEVONthink Search Query Syntax

## Query Structure

A query is one or more criteria evaluated left to right. Each criterion is either a bare term (matches all string fields) or a prefixed expression (`prefix<operator>value`). Combine criteria with boolean operators. Default boolean: `AND`.

```
<bare_term> [OPERATOR <bare_term>]
<prefix><operator><value> [OPERATOR <prefix><operator><value>]
```

Bare terms (no prefix) must appear at the start of a criterion or sub-criterion:
`test width>128 width<1024` — valid.
`width>128 test` — the bare term `test` should come first.


---

## 1. Boolean Operators

Evaluated in this priority: `()` > `"phrases"` / hyphens > `BEFORE` / `AFTER` / `NEAR` (with optional `NOT`) > `NOT` > `AND` / `OR` / `XOR`. Same-priority operators evaluate left to right.

| Syntax | Meaning |
|---|---|
| `term1 AND term2` | Both required (implicit default) |
| `term1 OR term2` | Either one |
| `term1 XOR term2` | Exactly one, not both |
| `NOT term` | Excludes term |
| `term1 NOT term2` | term1 required, term2 excluded |
| `term1 AND NOT term2` | Same as above (explicit form) |
| `"term1 term2"` | Exact phrase |
| `term1 OPT term2` | term1 required; term2 optional but boosts ranking |
| `term1 NEAR term2` | term1 within 10 words of term2 (either direction) |
| `term1 NEAR/n term2` | term1 within n words of term2 |
| `term1 BEFORE term2` | term1 appears before term2 |
| `term1 BEFORE/n term2` | term1 appears at most n words before term2 |
| `term1 AFTER term2` | term1 appears after term2 |
| `term1 AFTER/n term2` | term1 appears at most n words after term2 |
| `~term` | Contains term, including as a substring within a word |

Use parentheses to override precedence: `(Steve NEAR Jobs) AND iMac AND NOT MacBook`

Complex example:
```
Paracetamol NEAR (~effect OR impact) AND ((side OR second*) NEAR/2 ~effect)
```
Finds documents with "Paracetamol" near words containing "effect" or matching "impact", AND "side" or words starting with "second" within 2 words of a word containing "effect". Demonstrates nested parentheses, `~` with `NEAR`, and wildcards inside boolean expressions.


---

## 2. Wildcards

Wildcards apply within `:` (matches) expressions. NOT supported with `:<`, `:>`, `:~` operators.

| Pattern | Matches |
|---|---|
| `?` | Exactly one character |
| `*` | Zero or more characters |
| `[a-z]` | One character in range a–z |
| `[abc]` or `[a\|b\|c]` | One character from set |
| `[^abc]` | One character NOT in set |

Regular expressions are NOT supported.

### Wildcards vs. Contains (`~`) Operator

Given a document containing `DEVONtechnologies makes great software`:

| Query | Matches? | Why |
|---|---|---|
| `text:~tech` | ✅ | Contains operator finds "tech" as substring in any word |
| `text:tech` | ❌ | No whole word "tech" exists |
| `text:tech*` | ❌ | No word beginning with "tech" |
| `text:*tech` | ❌ | No word ending with "tech" |
| `text:*tech*` | ✅ | Wildcard matches "tech" inside "DEVONtechnologies" |

Unless you have a specific reason for wildcards, the `:~` operator is more succinct for substring matching.

### Character-Range Examples

Given text `He made a cake. She is making cookies. They live in Madeira`:
- `text:ma[dk]*` — matches "made", "making", "Madeira"
- `text:ma[dk]?` — matches only "made"

Given a document named `2024-2-14_Big Light Electric`:
- `name:[0-9]*` — matches all numbers regardless of length
- `name:[0-9][0-9]*` — matches two or more consecutive digits: "14", "2024"
- `name:202[0-9]` — matches documents from the 2020s


---

## 3. Prefix Operators (how prefix connects to value)

### 3a. String Operators

| Operator | Condition | Wildcards | Example |
|---|---|---|---|
| `:` | matches (supports wildcards) | yes | `name:report*` |
| `:!` | does not match | yes | `name:!draft*` |
| `==` | is (exact match) | no | `extension==pdf` |
| `!=` | is not (exact) | no | `md_language!=en` |
| `:<` | begins with | no | `docSubject:<party` |
| `:>` | ends with | no | `md_locality:>field` |
| `:~` | contains | no | `name:~tech` |

### 3b. Numeric Operators

| Operator | Condition | Example |
|---|---|---|
| `==` | equals | `rating==5` |
| `!=` | not equal | `label!=0` |
| `<` | less than | `wordcount<1000` |
| `<=` | less than or equal | `size<=10 MB` |
| `>` | greater than | `hits>0` |
| `>=` | greater than or equal | `width>=1920` |
| `:`low`-`high | range (inclusive) | `wordcount:500-1000` |

Range syntax `prefix:low-high` is equivalent to `prefix>=low prefix<=high`. Works with: `width`, `height`, `duration`, `length`, `hits`, `wordcount`, `charactercount`, `size`, `rating`.

### 3c. Date Operators

| Operator | Condition | Example |
|---|---|---|
| `:` | is (named period) | `created:Today` |
| `:!` | is not (named period) | `dueDate:!Today` |
| `<` | before | `modified<2024-01-01` |
| `<=` | before or on | `created<=2023-12-31` |
| `>` | after | `added>2024-06-01` |
| `>=` | on or after | `creationDate>=2019-01-31` |
| `:#`n`days` | within last n days | `opened:#7days` |
| `:!#`n`days` | NOT within last n days | `openingDate:!#30days` |

Named periods (case-insensitive): `Today`, `Yesterday`, `This Week`, `Last Week`, `This Month`, `Last Month`, `This Quarter`, `Last Quarter`, `This Year`, `Last Year`.

IMPORTANT: Weekday names are NOT supported (e.g. `This Monday`, `Last Sunday` will NOT work). Calculate the actual date instead.

Date formats — all equivalent:
```
additionDate>2019-03-10
additionDate>March 10, 19
additionDate>10 march, 2019
additionDate>2019-03-10 08:30:00 -0500
```
Time is optional. ISO 8601 (`YYYY-MM-DD`) recommended.


---

## 4. Search Prefixes

### 4a. Text & Metadata Content

| Prefix | Searches |
|---|---|
| `text` | Text contents of a file (body text) |
| `metadata` | All metadata fields combined (name, URL, comment, authors, recipients, title, description, subject, headline, keywords, organization, copyright, album, composer, creator, producer) — excludes body text |
| `name` | Record name (distinct from filename; no extension) |
| `filename` | Filesystem filename including extension |
| `extension` | File extension only (e.g. `txt`, `pdf`). Also supports `any` to filter by presence/absence of extension |
| `url` | Associated URL |
| `comment` | Finder comment |
| `aliases` | Aliases applied to an item |
| `tags` | Tags applied to an item. Separate multiple tags with semicolons: `tags:sync; methods` |

### 4b. Document-Specific Metadata

| Prefix | Searches |
|---|---|
| `docAuthors` | Sender (email) or author (document) |
| `docAuthorEmailAddresses` | Email address of sender |
| `docRecipients` | Recipient name (email) |
| `docRecipientEmailAddresses` | Email address of recipient |
| `docTitle` | Document title (may differ from name, e.g. MP3 song title) |
| `docComment` | Document-internal comments (RTF, PDF) |
| `docSubject` | Email subject line |
| `docHeadline` | Headline metadata |
| `docDescription` | Description metadata (e.g. image description) |
| `docKeywords` | Document-specific keywords (PDF, RTF) |
| `docOrganization` | Company/organization in metadata |
| `docCopyright` | Copyright information |
| `docAlbum` | Album info (media files) |
| `docComposer` | Composer info (media files) |
| `docCreator` | Application used to create the file |
| `docProducer` | Producer of the file (media files) |

### 4c. Numeric Properties

| Prefix | Value type | Notes |
|---|---|---|
| `width` | points (px × 72) | Document width |
| `height` | points (px × 72) | Document height |
| `length` | integer / seconds | Page count OR media length in seconds |
| `duration` | seconds | Media duration |
| `size` | bytes (or KB/MB/GB) | File size. Example: `size>=50 MB` |
| `wordcount` | integer | Word count. `wordcount>0` = has searchable text; `wordcount==0` = no text (useful for checking if PDF is OCRed) |
| `charactercount` | integer | Character count |
| `hits` | integer | Times opened/viewed |
| `rating` | 0–5 | Star rating |
| `label` | 0–7 or name | Color label. 0 = none. Name form: e.g. `label:Important` |

### 4d. Kind Prefix

`kind:` filters by document type. IMPORTANT: When the user wants documents/files/content but does not specify a type, always include `kind:document` to exclude groups, tags, and smart groups.

Supported values: `document`, `group`, `smartgroup`, `tag`, `ordinarytag`, `grouptag`, `text`, `rtf`, `formattednote`, `markdown`, `html`, `webarchive`, `xmlfile`, `propertylist`, `image`, `pdf`, `pdf+text` (OCRed/searchable PDFs), `quicktime`, `video`, `audio`, `bookmark`, `feed`, `news`, `script`, `sheet`, `email`, `other`.

### 4e. Date Prefixes

| Prefix | Short form | Meaning |
|---|---|---|
| `creationDate` | `created` | Date the item was created |
| `modificationDate` | `modified` | Date the item was last modified |
| `additionDate` | `added` | Date the item was added to the database |
| `openingDate` | `opened` | Date the item was last opened |
| `dueDate` | `due` | Reminder due date |

### 4f. Item State Prefix

All use the form `item:<state>`. Negation: `item:!<state>`.

**Matches items that are:**

| State | Meaning |
|---|---|
| `replicated` | Item is a replicant |
| `duplicated` | Item is a duplicate |
| `indexed` | Item is indexed (not imported) |
| `pending` | Contents not yet downloaded |
| `tagged` | Has tags applied |

**Matches items that have/contain a property:**

| State | Meaning |
|---|---|
| `aliases` | Has aliases set |
| `annotation` | Has an associated annotation file |
| `comment` | Has a Finder comment |
| `data` | Has content (filesize > 0, or group has children). `item:!data` = empty |
| `metadata` | Has metadata |
| `reminder` | Has a reminder set |
| `script` | Has an applied script |
| `thumbnail` | Has a thumbnail |
| `url` | Has a URL set |

**Marking states:**

| State | Meaning |
|---|---|
| `flagged` / `unflagged` | Flag state |
| `read` / `unread` | Read/unread state |
| `locked` / `unlocked` | Lock state |

### 4g. Miscellaneous Prefixes

| Prefix | Meaning |
|---|---|
| `md_attachments` | Number of email attachments or RTFD resources |
| `md_annotationcount` | Number of PDF annotations |
| `md_encrypted` | PDF encrypted state (numeric boolean: `md_encrypted==1`) |
| `md_incomingItemLinkCount` | Count of item links pointing TO this document |
| `md_outgoingItemLinkCount` | Count of item links FROM this document |
| `md_language` | Detected language abbreviation (e.g. `en`, `de`, `fr`) |
| `md_country` | Country from geolocation (e.g. `US`, `DE`, `FR`) |
| `md_zipcode` | Postal code from geolocation |
| `md_area` | State/province/region from geolocation |
| `md_locality` | City from geolocation |

### 4h. Custom Metadata Prefixes

User-defined custom metadata attributes are searchable. The prefix is formed by: `md` + attribute name in lowercase with spaces removed.

Example: attribute "Total Cost" → prefix `mdtotalcost`. Query: `mdtotalcost>1000`.


---

## 5. Sub-Criteria

Curly braces `{ }` define sub-criteria for complex grouped queries. Prefix sub-criteria with `any:` (match any criterion = OR logic) or `all:` (match all criteria = AND logic). Default is `all`.

```
any: name:test OR imprint {any: tags:blue; red}
```

This finds items whose name matches "test" OR contains "imprint", or that have tags "blue" or "red".


---

## 6. White Space & Hyphenation Rules

- Hyphenated words (`page-index`) and underscored words (`page_id`) are treated as phrases: equivalent to `"page index"` / `"page id"`.
- Specifically: `word1-word2` or `word1_word2` → `word1word2 OR "word1 word2"`.
- Dot-separated characters are treated as abbreviations: `t.a.t.u` → `"t a t u" OR tatu`.


---

## Quick Reference: Common Query Patterns

```
All PDFs tagged "invoice" from 2024:
  kind:pdf tags:invoice created>=2024-01-01 created<2025-01-01

Unread flagged items:
  item:flagged item:unread

Large images (>5MB) without searchable text:
  kind:image size>5 MB

Documents modified this week containing "report":
  report kind:document modified:This Week

Items added in last 30 days, not yet tagged:
  item:!tagged added:#30days

PDFs with annotations:
  kind:pdf md_annotationcount>0

Searchable (OCRed) PDFs:
  kind:pdf+text

Non-searchable PDFs (need OCR):
  kind:pdf wordcount==0

Items by a specific author, rated 4+:
  docAuthors:Smith rating>=4
```
