# `GeoGenius.Query`
[🔗](https://github.com/agoodway/geo_genius/blob/main/lib/geo_genius/query.ex#L1)

Composable Ecto queries over the catalog's read functions.

`GeoGenius`'s struct-returning functions answer one question per call. These
return queries instead, so a host can compose one further before running it.

Every read here goes through a `SETOF` plpgsql function. PostgreSQL cannot
push a predicate, a join qualifier, or a `LIMIT` inside one, so the whole set
materialises before a host's own `WHERE` runs, and the selects below project
five of `area_match`'s sixteen columns -- `release_id` among the eleven they
drop. **Joining catalog areas against a host's own table and aggregating
belongs in `GeoGenius.Published`**, whose read-only schemas over the
`published_*` views give the planner an ordinary relation and the caller
every column. A host projection keyed `(release_id, area_key)` is joinable
only from there.

What these serve, and a view has no form for: a walk deeper than one relation
hop through `:max_depth`, and `search_areas/2`'s trigram ranking. An
unpublished release is served either way -- here through `:release_id`, and
there through the option of the same name, which swaps the schemas onto the
release-scoped base views.

Ecto requires a fragment's SQL to be a literal, so the schema prefix is read
at compile time via `Application.compile_env/3` and validated the same way
every other path validates a prefix, through the package's internal prefix
validation. It is emitted as a quoted identifier, because that validation
accepts reserved words: a host installed at `prefix: "user"` needs
`"user".children_of(...)`, which is what these fragments build.

`:prefix` therefore belongs in `config/config.exs`, and must never be set in
`config/runtime.exs`. `Application.compile_env/3` records the result of its
read, and "not configured" is one of the results it records; Elixir raises at
boot when the value present then differs from the recorded one. A `:prefix`
first assigned in `runtime.exs` differs from a compile-time read that found
nothing even when the value assigned is the identical default string, so the
host fails to boot. `:repo` carries no such constraint and may be set
wherever the host prefers. A changed compile-time `:prefix` takes effect once
`:geo_genius` is recompiled, with `mix deps.compile geo_genius --force`.

There is no per-call `:prefix` option here, and there cannot be one: a
runtime value cannot fill a position `fragment/1` requires to be a literal.
Passing `:prefix` in `opts` raises `ArgumentError` rather than being
silently ignored. A host that serves several catalogs from one VM and needs
a runtime `:prefix` per call must use the struct-returning API in
`GeoGenius` instead, which resolves `:prefix` through `GeoGenius.Context` on
every call.

# `ancestors_of`

```elixir
@spec ancestors_of(String.t(), keyword()) :: Ecto.Query.t()
```

Areas above this one, as a query. Same options as `children_of/2`.

# `areas_by_code`

```elixir
@spec areas_by_code(String.t(), String.t(), keyword()) :: Ecto.Query.t()
```

Areas carrying a code, as a query.

Accepts `:collections`, `:types`, `:release_id`, `:include_retired`,
`:parent_area_key`, and `:parent_max_depth`. `:release_id` accepts the same
two shapes as `children_of/2`.

# `children_of`

```elixir
@spec children_of(String.t(), keyword()) :: Ecto.Query.t()
```

Areas below this one, as a query.

Accepts `:types`, `:classifications`, `:max_depth`, `:release_id`,
`:include_retired`. `:release_id` accepts either the hyphenated string
every read projects or the sixteen raw bytes Postgrex binds.

# `search_areas`

```elixir
@spec search_areas(String.t(), keyword()) :: Ecto.Query.t()
```

Areas ranked by name similarity, as a query.

Accepts `:collections`, `:types`, `:limit`, `:release_id`, and
`:include_retired`. `:release_id` accepts the same two shapes as
`children_of/2`. `:limit` defaults to 50 and takes `nil` for no cap, which
is what a caller narrowing the ranking afterwards asks for.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
