# Named queries

Every read of stored records is a named query. An entity is a kind of
stored record, like a table with types ([Entities](entities-and-the-id.md)).
A query is one read of an entity: which records, in what order, and how
many. Each query lives in a file of its own in its domain's `queries/`
folder ([Files and names](files-and-names.md)).
You call it by name, as you call an action, the function that changes
records ([Functions and actions](functions-and-actions.md)).

This query gives an owner's todos a page at a time, newest first. Its
`filter` parameter is a `ListTodos`, an `Input` type a route fills from the
request: an optional `status`, an optional `due_before`, a `page_size`
that defaults to 25 and an optional `after`, the cursor the previous page
returned:

```jadpo
// domains/planning/queries/todos_for_owner.jadpo
/// The owner's visible todos, newest first, one page at a time.
query todos_for_owner(owner_id: User.id, filter: ListTodos) -> Page<TodoView> {
    from: Todo
    where: Todo.owner_id == owner_id
    where_present: Todo.status == filter.status
    order: [Todo.created_at desc]
    page: {
        size: filter.page_size
        after: filter.after
    }
}
```

`from:` names the entity, `where:` the records it keeps, `order:` their
order and `page:` how many come back at once and where the page starts.
`where_present:` applies its condition only when the value on the right
is there: when the request leaves out `status`, every status comes back.
The [Filters](filters.md) and [Order and pages](order-and-pages.md) topics
cover each section in full.

## How many records come back

The result type says how many records come back, so there is no separate
word for it. A query for exactly one record also needs `missing:`, which
names the failure it gives when no record matches
([`missing:`](turning-one-failure-into-another.md#a-read-that-finds-nothing-missing)):

| Result type | Returns | Needs |
|---|---|---|
| `TodoView` | exactly one record | a `where:` on a unique field, and `missing:` |
| `TodoView?` | zero or one | a `where:` on a unique field |
| `List<TodoView>` | up to the number `limit:` sets | `order:` and `limit:` |
| `Page<TodoView>` | one page and a cursor for the next | `order:` and `page:` |
| `FeatureCollection<Point, TodoPin>` | a map layer of up to the number `limit:` sets, each record a point on the map ([A query giving a map layer](a-query-giving-a-map-layer.md)) | `geometry:`, `order:` and `limit:` |

Some apps are shared by several tenants, such as companies, whose records
are kept apart ([Tenancy](tenancy.md)). There a `unique:` rule
holds within each tenant: two companies can each have a project called
Launch ([A unique rule holds within its tenant](tenancy.md#a-unique-rule-holds-within-its-tenant)).
So a unique field alone doesn't pick one record. A query for one record
compares the record's tenancy reference in its `where:` as well as the
unique field, unless it compares a reference to another record of the
same tenant, which already picks the tenant.

## What a result holds

A result's element type is the entity, or a type built from its fields.
`type TodoView = Output { pick: [Todo.id, Todo.title] }` is a type for
output that picks two of `Todo`'s fields
([Building a type from other types](building-a-type-from-other-types.md)).
The compiler reads only the fields that type needs.

That type can't have a rule across its fields, such as "`ends_at` is
after `starts_at`" ([Rules across fields](objects.md#rules-across-fields)).
This holds even when each field's own limits would make the rule always
true. The same goes for every other type that holds a query's records,
including a `Nearest<T>`'s item ([Nearest first](order-and-pages.md#nearest-first)), a map layer's properties,
an aggregate's rows and the related records a query brings with it. A
result type with such a rule is the error
[`query.result_rule`](../errors/query.result_rule.md).

The reason is that Jadpo checks a rule whenever a value is made, and a
broken rule is a failure your code handles with `attempt`
([Passing a failure on](passing-a-failure-on-attempt.md)). A
read has no `attempt` around it, and the database can't check the rule as
it reads. So a stored record that broke the rule could only be a
[defect](faults.md#defects), a bug rather than a failure the caller
handles. Instead, read into a type with the same fields and no rule, then
build the type with the rule from it in code, inside an `attempt`.

Records the entity's [lifecycle](lifecycle.md) hides, such as
soft-deleted todos, are left out of every query.

A query writes nothing and calls no actions
([Functions and actions](functions-and-actions.md)). Call it by
its bare name in its own domain. From another domain, put the domain's
name first, `planning.todos_for_owner(...)`, which works when the query
is `public`.

## The statement it sends

`jadpo sql todos_for_owner` prints the SQL statement the query sends. It
is one `SELECT` with the query's conditions, an `ORDER BY`, and a `LIMIT`
one more than the page's size, so the runtime knows whether a next page
exists. It selects only the columns the result type needs, plus the
`order:` fields a page cursor needs and any field a
[policy](policy.md) condition checks outside the database.

The statement's text is fixed when the project is compiled. A
`where_present:` condition and the cursor are written
`($n IS NULL OR ...)`, so the same query always sends the same statement.

A query called inside an action reads in the action's transaction. Called
from a test, it runs in a short transaction of its own.
