# Search

Give an entity a `search:` section and people can search its records by
words. The compiler generates the index, keeps it up to date and
generates its migration, and a query finds records with `matches`. By
default the search runs on PostgreSQL's full-text search, so you need
nothing else. Search is the `search` capability, so an engine's package,
such as Meilisearch, Typesense, Algolia or OpenSearch, can fill it
instead.

```jadpo
// domains/planning/entities/todo.jadpo
entity Todo {
    ...
    search: {
        fields: [title, notes]
        language: SearchLanguage.english
    }
}

// domains/planning/queries/search_todos.jadpo
query search_todos(owner_id: User.id, search_phrase: SearchText) -> Page<TodoView> {
    from: Todo
    where: Todo.owner_id == owner_id
    where: Todo matches search_phrase
}
```

- Fields listed first count most when ordering results.
- With no `order:`, the best matches come first, then by `id`, in keyset
  pages like every other list ([Pages](order-and-pages.md#pages)).
- `search_phrase` takes what people type into search boxes: words,
  quoted phrases and `-word`, never raw query syntax.
- `search:` replaces the full-text index `kind:`
  ([Special kinds of index](indexes.md#special-kinds-of-index)): one
  declaration, not an index plus hand-written conditions.

## An engine instead of PostgreSQL

`search: { fields: [...]  store: products_index }` names a connection
whose package provides `search`. Leave `store:` out and the search is
PostgreSQL's. The engine's package does the whole job: it upserts a
record's searchable fields, removes a record, and queries by words with
equality filters.

```jadpo
// domains/catalogue/entities/product.jadpo
entity Product {
    ...
    search: {
        fields: [name, description]
        store: products_index
    }
}
```

- **Only equality filters reach an engine.** A `matches` query on an
  entity whose search uses an engine may only have `where:` lines that
  test equality on fields the index holds. Anything else is a compile
  error naming PostgreSQL search, which can filter any way.
- **Ids from the engine, records through policy.** The engine returns
  ranked ids, already filtered by tenant. Jadpo then reads those records
  through the query, with policy applied as for any query
  ([Policy](policy.md)), so a result is never one the caller couldn't
  read.
- **Kept up to date after commit.** Every committed change to a
  searchable field reaches the engine through the outbox, and is retried
  like any delivery
  ([Delivery: at least once, safe to repeat](delivery-at-least-once-safe-to-repeat.md)).
  Erasing a person removes their records from the index
  ([Erasure](erasure.md)). `jadpo search reindex <entity>`
  rebuilds the index.
- **Engine features** (synonyms, ranking rules, typo tolerance settings)
  are the package's own operations, called from a domain the connection
  lists.
- **Development and tests use PostgreSQL search** whatever production
  uses, so nothing leaves the machine and no engine is needed to run a
  test.
