GeoSQL.QueryUtils (GeoSQL v1.3.0)
View SourceUtilities to make queries easier to produce
Summary
Functions
Turn a set of options into named parameters using the =>
parameter naming operator,
checking that the keys are allowed.
Turn a set of options into positional parameters, checking that the keys are allowed.
Used to cast data to geometries in a backend-neutral fashion. This can be necessary when, for example, passing geogrpahy types to geometry functions in PostGIS.
When returning a single geometry in an Ecto query select
clause, geometries may
be returned as raw binaries in some backends.
When returning geometries as part of a map or list Ecto query select
clause (as opposed to
returning an Ecto.Schema
struct, where geometries are automatically decoded), geometries may
be returned as raw binaries in some backends.
Used to wrap WKB literals in a portable fashion. When used in an ecto query, the return value will need to be pinned in the query.
Functions
@spec as_named_params(options :: Keyword.t(), allowed_keys :: [:atom]) :: {param_string :: String.t(), params :: list()}
Turn a set of options into named parameters using the =>
parameter naming operator,
checking that the keys are allowed.
This can only be used with functions that actually have named paramters, which is mostly user-provided functions.
@spec as_positional_params(options :: Keyword.t(), allowed_keys :: [:atom]) :: {param_string :: String.t(), params :: list()}
Turn a set of options into positional parameters, checking that the keys are allowed.
This makes it easier to implement support for SQL functions which support a variable number
of options (often flags to enable/disable functionality). The user can provide only the
parameters they wish to provide, and this will turn them into a template string with the
necessary number of ?
s and the values included.
The allowed_keys
must be provided in the order they appear in the SQL function.
Example
For an SQL function my_func(int foo, string bar)
with variant
my_func(int foo, stering bar, boolean baz)
:
foo = 100
valid_params = [:bar, :baz]
{param_string, params} = GeoSQL.QueryUtils.as_positional_params([foo: 100, bar: "names"], valid_params)
{", ?", ["names"]}
from(r in Schemna, where: fragment()"my_func(?#{param_string})", [foo | params])
Used to cast data to geometries in a backend-neutral fashion. This can be necessary when, for example, passing geogrpahy types to geometry functions in PostGIS.
When returning a single geometry in an Ecto query select
clause, geometries may
be returned as raw binaries in some backends.
To get Geo
structs from such queries, pass the results of the query to decode_geometry/2
to guarantee corectly decoded geometries regardless of the backend being used.
from(location in Location, select: MM.boundary(location.geom))
|> Repo.all()
|> QueryUtils.decode_geometry(Repo)
@spec decode_geometry( term(), Ecto.Repo.t(), [non_neg_integer()] | [String.t()] | [:atom] ) :: term()
When returning geometries as part of a map or list Ecto query select
clause (as opposed to
returning an Ecto.Schema
struct, where geometries are automatically decoded), geometries may
be returned as raw binaries in some backends.
To get Geo
structs from these queries, pass the results of the query to decode_geometry/3
.
The third fields_to_decode
parameter is a list of the fields to decode. When the query
results are lists or tuples, the fields_to_decode
must be a list of integers that are the indexes
of the elements to decode. In the case of maps, fields_to_decode
should be the keys (strings or
atoms) in the map that contain geometry data.
This function is intentionally liberal with what it accepts. Instead of creating errors, it will instead return the original data in the fields that failed to decode. As real-world query returns can be noisy and differ between backends, this is a more usable approach in practice than returning either error tuples or letting exceptions bubble up.
from(location in Location, select: [location.name, MM.boundary(location.geom)])
|> Repo.all()
|> QueryUtils.decode_geometry(Repo, [1])
When returning a map, fields_to_decode
must be a list of field names:
from(location in Location, select: %{name: location.name, boundary: MM.boundary(location.geom)]})
|> Repo.all()
|> QueryUtils.decode_geometry(Repo, ["boundary])
@spec wrap_wkb(wkb :: binary(), Ecto.Repo.t()) :: GeoSQL.QueryUtils.WKB.t()
Used to wrap WKB literals in a portable fashion. When used in an ecto query, the return value will need to be pinned in the query.
Example:
from(g in GeoType,
select: g.linestring == MM.linestring_from_wkb(^QueryUtils.wrap_wkb(wkb, MyApp.Repo))