Ecto:如何在更新中调用函数

Ecto: how to call a function in an update

我正在尝试创建一个迁移,通过对该列中的每个项目调用函数 date_str_to_timestamp(在同一文件中定义)将列中的数据转换为不同的格式。

from(p in "posts",
     update: [set: [ timestamp: ^date_str_to_timestamp(p.time) ]])
|> Pesterbot.Repo.update_all([])

然而我得到这个:

 _build/dev/lib/pesterbot/priv/repo/migrations/20170414014413_posts_to_messages.exs:34

** (Ecto.Query.CompileError) `^(p.time(), date_str_to_timestamp)` is not a valid query expression.

* If you intended to call a database function, please check the documentation
  for Ecto.Query to see the supported database expressions

* If you intended to call an Elixir function or introduce a value,
  you need to explicitly interpolate it with ^

    (ecto) expanding macro: Ecto.Query.update/3
    _build/dev/lib/pesterbot/priv/repo/migrations/20170414014413_posts_to_messages.exs:33: Pesterbot.Repo.Migrations.PostsToMessages.up/0
    (ecto) expanding macro: Ecto.Query.from/2
    _build/dev/lib/pesterbot/priv/repo/migrations/20170414014413_posts_to_messages.exs:33: Pesterbot.Repo.Migrations.PostsToMessages.up/0
    (elixir) expanding macro: Kernel.|>/2
    _build/dev/lib/pesterbot/priv/repo/migrations/20170414014413_posts_to_messages.exs:35: Pesterbot.Repo.Migrations.PostsToMessages.up/0

编辑:@Dogbert

要求的 date_str_to_timestamp 来源
def date_str_to_timestamp(date_str) do
    {:ok, datetime} = Timex.parse(date_str, "{UNIX}")
    datetime |> Elixir.DateTime.to_unix(:millisecond)
  end
end

编辑: timestamp 将是自相应 time 值的纪元以来的毫秒整数,现在是 UNIX 格式的字符串,例如:Mon Mar 20 19:49:43 CDT 2017

所以Mon Mar 20 19:49:43 CDT 2017应该变成1490057383000

您不能在这样的 UPDATE 查询中调用 Elixir 函数,因为数据库必须能够计算表达式本身。幸运的是,PostgreSQL 确实有函数可以完成你的 Elixir 函数所做的事情:

iex(1)> A.date_str_to_timestamp "Tue Mar 5 23:25:19 PST 2013"
1362554719000
postgres=# select EXTRACT(EPOCH FROM 'Tue Mar 5 23:25:19 PST 2013'::timestamp) * 1000;
   ?column?
---------------
 1362525919000
(1 row)

您可以像这样在 UPDATE 查询中使用它:

from(p in "posts",
     update: [set: [timestamp: fragment("EXTRACT(EPOCH FROM ?::timestamp) * 1000", p.time)]])