Phoenix 1.3.0+Ecto 2.1中Postgres数组列整数值无法正常获取
看起来你遇到的问题是Ecto解析borrower_ids数组字段时返回了异常值'$',而相同格式的owner_ids却能正常工作。结合你的场景,我整理了几个可能的原因和对应的排查/解决步骤:
1. 先确认数据库字段的实际类型
虽然你说psql里看起来格式一致,但有可能borrower_ids和owner_ids的数据库类型并不相同。Ecto对类型匹配要求很严格,如果字段定义是{:array, :integer}但实际数据库里是text[]类型,就会出现解析异常。
你可以在psql里执行这条命令来验证:
SELECT column_name, data_type, udt_name FROM information_schema.columns WHERE table_name = 'books' AND column_name IN ('owner_ids', 'borrower_ids');
正常情况下,两个字段的udt_name都应该是_int4(对应PostgreSQL的integer[])。如果borrower_ids的udt_name是_text,那说明是类型不匹配导致的问题。
解决方法:
如果是类型不匹配,你有两种选择:
- 如果你想保持数据库为
text[],可以把Schema里的字段改成{:array, :string},之后再手动转换为整数列表:defmodule Book do use Ecto.Schema schema "books" do field :title, :string field :owner_ids, {:array, :integer} field :borrower_ids, {:array, :string} field :published, :boolean end # 新增一个虚拟方法来获取整数格式的borrower_ids def borrower_ids_as_integers(%__MODULE__{borrower_ids: ids}) do Enum.map(ids, &String.to_integer/1) end end - 或者直接修改数据库字段类型为
integer[]:ALTER TABLE books ALTER COLUMN borrower_ids TYPE integer[] USING borrower_ids::integer[];
2. 检查字段是否包含异常数据
即使类型一致,也有可能borrower_ids字段里存在Ecto无法解析的异常值(比如NULL元素、非整数字符串等)。你可以直接查询目标行的具体值:
SELECT borrower_ids FROM books WHERE id = (SELECT id FROM books LIMIT 1);
如果结果里有类似{NULL}或者{'abc'}这样的内容,就会导致Ecto解析失败,返回异常值。
解决方法:
清理异常数据,确保数组里的所有元素都是合法的整数。
3. 尝试显式指定Ecto的加载策略
Ecto 2.x的数组处理存在一些小bug,你可以尝试在Schema字段里显式添加load_in_query: true选项,强制Ecto在查询阶段就完成类型转换:
defmodule Book do use Ecto.Schema schema "books" do field :title, :string field :owner_ids, {:array, :integer} field :borrower_ids, {:array, :integer}, load_in_query: true field :published, :boolean end end
这个选项会让Ecto在SQL查询中直接转换类型,避免客户端解析时出现问题。
4. 考虑升级Ecto版本
Ecto 2.1是比较老旧的版本(发布于2017年),后续的2.2+版本修复了很多数组相关的bug。Phoenix 1.3也兼容Ecto 2.2及以上版本,升级可能会直接解决这个问题。
你可以修改mix.exs里的依赖:
defp deps do [ {:phoenix, "~> 1.3.0"}, {:ecto, "~> 2.2"}, # 其他依赖... ] end
然后执行mix deps.update ecto完成升级。
内容的提问来源于stack exchange,提问作者Gokul

