Phoenix / Ecto - 查询字符串数组中的匹配项

时间:2017-06-19 01:44:31

标签: postgresql elixir phoenix-framework ecto

在我的一个凤凰应用程序表中,我有一个字符串数组的字段。我希望能够使用OpenCSVSerde类型的查询来查看数组中的任何值是否包含查询字符串 - 但是,我不知道如何做到这一点。在应用程序的上一次迭代中,有问题的字段只是一个字符串字段,以下查询完美地运行:

where: like()

现在我已将results = from(u in User, where: like(u.fulltext, ^("%#{search_string}%")) |> Repo.all() 字段更改为字符串数组(fulltext,在Postgres术语中),此查询可以理解为失败并显示错误

character varying(255)[]

但我不确定如何优化查询以匹配新架构。

例如,用户的ERROR 42883 (undefined_function): operator does not exist: character varying[] ~~ unknown 字段将显示为

fulltext

并且["john smith", "john@test.com"] search_string"john""@test"等时应返回相关记录 - 如果"n smith"与任何一方相匹配列表值。

简单来说,查询会读取类似“返回记录,其中search_string列表中的值search_string”。

我可以想到各种“hacky”解决方法,比如只返回所有用户的列表,然后使用一些链式u.fulltext函数来运行它们并检查Enum.map的值以进行部分匹配,但如果使用Ecto的查询语法有一个更优雅的解决方案,我宁愿选择它。任何人都可以提供任何指导吗?

2 个答案:

答案 0 :(得分:2)

您可以在PostgreSQL中使用带有子查询的unnest来检查数组中的任何项是否为LIKE something

from(p in Post, select: p.tags, where: fragment("exists (select * from unnest(?) tag where tag like ?)", p.tags, "%o%")

在您的情况下,这应该有效:

from(u in User, where: fragment("exists (select * from unnest(?) tag where tag like ?)", u.fulltext, ^("%#{search_string}%"))
iex(1)> Repo.insert! %Post{tags: ~w(foo bar baz)}                                                                              [debug] QUERY OK db=0.3ms
iex(2)> Repo.insert! %Post{tags: ~w(quux)}
iex(3)> Repo.insert! %Post{tags: ~w(hello world)}
iex(4)> query = "%o%"
"%o%"
iex(5)> Repo.all from(p in Post, select: p.tags, where: fragment("exists (select * from unnest(?) tag where tag like ?)", p.tags, "%o%"))
[debug] QUERY OK source="posts" db=3.9ms
SELECT p0."tags" FROM "posts" AS p0 WHERE (exists (select * from unnest(p0."tags") tag where tag like '%o%')) []
[["foo", "bar", "baz"], ["hello", "world"]]

答案 1 :(得分:2)

您可以使用fragmentunnest将数组转换为连接:

user_texts = 
  from u in User, 
  select: %{id: u.id, fulltext: fragment("unnest(fulltext)")}

query = 
  from u in User, 
  join: t in subquery(user_texts), on: u.id == t.id, 
  where: like(t.fulltext, ^("%#{search_string}%")), 
  select: u,
  distinct: true

Repo.all(query)