Ecto - 将字符串数组转换为片段中的整数

Ecto - casting an array of strings to integers in a fragment

在 Ecto 查询中,我运行针对 Postgres (13.6),我需要将一个 id 列表插入 fragment - 这通常不是我遇到的问题,但在这种情况下,id 列表作为需要转换为整数(或更具体地说,BIGINT)的字符串列表接收。我认为需要的查询如下,麻烦的是ANY(ARRAY?::BIGINT[]):

ModelA
|> where(
  [ma],
  fragment(
    "EXISTS (SELECT * FROM model_b mb WHERE mb.a_id = ? AND mb.c_id = ANY(ARRAY?::BIGINT[]))",
    a.id,
    ^c_ids
  )
)

其中 c_ids 将是一个类似于 ["1449441579", "2345556834"]

的列表

然而,当我运行这个时,我得到了错误

(Postgrex.Error) ERROR 42703 (undefined_column) column "array" does not exist

指生成的SQL

ANY(ARRAY::BIGINT[])

当然,我可以在我的应用程序代码中预先将 c_ids 的数组转换为整数,但我想看看我是否可以将其转换为查询本身。

直接写片段 SQL 效果很好:

SELECT * FROM model_b mb WHERE mb.a_id = 1 AND mb.c_id = ANY(ARRAY['1449441579', '2345556834']::BIGINT[]);

使这种数组转换在 Ecto 片段中工作的惯用方法是什么?非常感谢。

我确实找到了一个 post,这让我找到了一个潜在的解决方案 - https://elixirforum.com/t/interpolating-lists-into-ecto-query-fragment-1/16690/7 - 但是,我不认为这对我来说是最佳的。

通过使用 Enum.join 并将我的初始字符串数组转换为字符串,然后允许 Postgres 将该字符串转换为 bigints 数组,结果如下:

ModelA
|> where(
  [ma],
  fragment(
    "EXISTS (SELECT * FROM model_b mb WHERE mb.a_id = ? AND mb.c_id = ANY(ARRAY?::BIGINT[]))",
    a.id,
    ^Enum.join(c_ids, ",")
  )
)

所以我post把它放在这里以供参考,因为它确实“有效”,但我觉得这样做有点傻,因为我特意试图避免在将列表传递给PG...所以是的,我仍然很想听听“正确”的方法...

为了整理我的评论,我会在查询之前进行整数转换。您可以使用 dynamic 宏来支持 IN 查询:

import Ecto.Query

alias YourApp.Repo
alias YourApp.SomeSchema, as: ModelA

strs = ["1", "2", "3"]

ids = Enum.map(strs, fn x -> String.to_integer(x) end)

conditions = dynamic([tbl], tbl.id in ^ids)

Repo.all(
  from ma in ModelA,
  where: ^conditions
)
|> IO.inspect()