Ecto, fragments, with ordinality de PostgreSQL
Tenía una query con un where in
:
DataStructureVersion
|> ...varias transformaciones de la query
|> where(q, [dsv], dsv.id in ^ids)
Sin un order_by
el orden no está definido. En mi caso, resultaba ser por defecto por id
ascendiente de DataStructureVersion
. En lugar de ello, quería conservar el mismo que el del array de ids
, porque proviene de una búsqueda de ElasticSearch que ya llega ordenada. Para conseguirlo, se puede utilizar WITH ORDINALITY de PostgreSQL:
defp enrich_order_by(query, ds_ids, :asc_data_structure_ids_with_ordinality) do
query
|> join(:inner, [dsv], ordinality in fragment("SELECT * FROM UNNEST(?::int[]) WITH ORDINALITY as ordinality(id, num)", ^ds_ids), on: dsv.data_structure_id == ordinality.id)
|> order_by([_dsv, ordinality], asc: ordinality.num)
end
Voy a contar de paso algo más. Sobre la query resultante tenía que hacer un distinct on.
Hay que incluir en éste las expresiones que estén más a la izquierda del order by. De lo contrario, saltará este error:
SELECT DISTINCT ON expressions must match initial ORDER BY expressions
DISTINCT ON ( expression [, …] ) keeps only the first row of each set of rows where the given expressions evaluate to equal. […] Note that the «first row» of each set is unpredictable unless ORDER BY is used to ensure that the desired row appears first. […] The DISTINCT ON expression(s) must match the leftmost ORDER BY expression(s).
https://stackoverflow.com/a/9795768
http://www.postgresql.org/docs/9.0/static/sql-select.html#SQL-DISTINCT
En este caso sólo hay una expresión, el campo ordinality.num
. Encadené la query anterior con:
defp distinct_by(query, %{order_by: :asc_data_structure_ids_with_ordinality}) do
distinct(query, [_dsv, ordinality], ordinality.num)
end