# Query language

Short pipelines in jq's shape, checked before they run.

Start with a collection. Add stages with pipes. End with a count, a quoted jq expression, or nothing. Every word is checked first, and a wrong one is refused by name.

[Reading records in](/haematite/query-language/sources/)

A collection by name, a bare name with filters, a match across relations, or a shape.
[Searching](/haematite/query-language/search/)

Text with rg and grep. Meaning with similar and rerank.
[Finishing](/haematite/query-language/bounds/)

sort, limit, window, group_by, percentile and count.
[Served words](/haematite/query-language/stages/)

Every word, as the service checks it.

## Six queries

**Read a ward**

```text
patients(ward: "4B")
```


**Newest two admissions**

```text
collection("patients") | sort(.admitted; desc) | limit(2)
```


**Two hops across relations**

```text
match (c:patients)-[:admitted_to]->(w:wards)<-[:staffed_by]-(n:nurses)
```


**Case-insensitive, fixed string**

```text
patients | rg(.ward; "4b"; "-i -F")
```


**Two collections in one shape**

```text
{ patients(ward: "3A") { name } wards { code } }
```


**Nearest by vector, then reranked**

```text
collection("patients") | similar(.embedding; record("p0"); dot) | limit(20) | rerank(.embedding; record("p0"); dot) | limit(2)
```


## When a query is wrong

The service answers with a code, a sentence and the span of text that caused it. A `sort` after the quoted jq stage, a `count` followed by anything, a `limit` of zero, a `percentile` of 101: each is refused by name.

> Try it from the command line
> `haematite query --config service.json --branch main '<query>'` prints each row on its own line. Exit code 2 is a refusal, with the message on standard error.
