GeoGenius.Query (geo_genius v0.2.0)

Copy Markdown View Source

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.

Summary

Functions

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

Areas carrying a code, as a query.

Areas below this one, as a query.

Areas ranked by name similarity, as a query.

Functions

ancestors_of(area_key, opts \\ [])

@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(code_type, code_value, opts \\ [])

@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(area_key, opts \\ [])

@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(query, opts \\ [])

@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.