# classify - is this the kind of document expected Source: https://truecopy.dev/docs/classify/ Module: truecopy/classify A statement quoting the word invoice in a transaction label must not be filed as an invoice. Precedence, stated rather than smuggled into an ordering. A bank statement quotes the word _invoice_ in a transaction label. Read in the wrong order it is filed as an invoice, and **every operation it carries leaves the budget with it**. An IBAN, or the debit/credit/balance vocabulary, settles the question before the word invoice is even looked at. ```ts import { classifyDocument, type DocumentKind } from 'truecopy/classify'; const KINDS: DocumentKind<'statement' | 'invoice'>[] = [ { name: 'statement', requires: [{ anyOf: [{ all: [IBAN] }, { all: [DEBIT, CREDIT] }] }] }, { name: 'invoice', requires: [{ anyOf: [{ all: [INVOICE_WORDS] }], absent: [IBAN] }] } ]; classifyDocument(text, KINDS); // 'statement' | 'invoice' | null ``` ## Three notions, and no more | | | | --------------- | ---------------------------------------------------------------------------------------- | | **pattern set** | `{ all: [...], occurrences?: n }`: all of its patterns, optionally several times | | **requirement** | `{ anyOf: [...sets], absent?: [...] }`: any one of its sets, and none of the absent ones | | **kind** | all of its requirements. **First match wins.** | `absent` is what makes precedence **stated** rather than smuggled into an ordering that means something else. _An invoice only when there is no IBAN_ is written down, in the kind itself, where somebody reading it a year later will find it. ## occurrences: one may be a quotation ```ts { all: [PENSION_VOCABULARY], occurrences: 2 } ``` A newspaper article about pensions mentions quarters, carries years and amounts, and would satisfy every other rule. A real record carries the vocabulary dozens of times. One occurrence may be a citation. Two rarely are. ## This is not the schema question `classify` asks _is this the kind of document expected at all_. [`schema`](https://truecopy.dev/docs/schema/) asks _does this reading carry what it must_. Two answers to one question always end up disagreeing, so they are two functions and the kind is asked first: a document of the wrong kind deserves no remark about its missing fields. ## Patterns as data The patterns here are regular expressions, and everything that varies by market, by issuer or by document family should be a **value** rather than a branch; see [pattern](https://truecopy.dev/docs/pattern/). A value can be served by a backend, refined by whoever reads the documents, versioned, and swapped for another market without a deploy. A branch can only be changed by whoever can change the code. Map of this site for a model: https://truecopy.dev/llms.txt Every page in one file: https://truecopy.dev/llms-full.txt