Phoenix/Ecto - 查询字符串数组中的匹配项
Phoenix/Ecto - query for matches in array of strings
在我的 Phoenix 应用程序的一个表中,我有一个字符串数组字段。我希望能够使用 where: like()
类型的查询来查看数组中的任何值是否包含查询字符串 - 但是,我不确定该怎么做。在应用程序的前一次迭代中,有问题的字段只是一个字符串字段,下面的查询完美运行:
results = from(u in User,
where: like(u.fulltext, ^("%#{search_string}%"))
|> Repo.all()
既然我已经将 fulltext
字段更改为字符串数组(character varying(255)[]
,在 Postgres 术语中),可以理解该查询失败并显示错误
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
匹配任何部分列表值之一。
在简单的英语中,查询将读作 "return records where a value like search_string
is found in the list u.fulltext
"。
我可以想到各种 'hacky' 解决方法,比如只返回所有用户的列表,然后使用一些链式 Enum.map
函数 运行 通过它们并检查 fulltext
用于部分匹配,但如果有使用 Ecto 查询语法的更优雅的解决方案,我宁愿选择它。任何人都可以提供任何指导吗?
您可以在 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"]]
您可以使用 fragment
和 unnest
将数组转换为连接:
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)
在我的 Phoenix 应用程序的一个表中,我有一个字符串数组字段。我希望能够使用 where: like()
类型的查询来查看数组中的任何值是否包含查询字符串 - 但是,我不确定该怎么做。在应用程序的前一次迭代中,有问题的字段只是一个字符串字段,下面的查询完美运行:
results = from(u in User,
where: like(u.fulltext, ^("%#{search_string}%"))
|> Repo.all()
既然我已经将 fulltext
字段更改为字符串数组(character varying(255)[]
,在 Postgres 术语中),可以理解该查询失败并显示错误
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
匹配任何部分列表值之一。
在简单的英语中,查询将读作 "return records where a value like search_string
is found in the list u.fulltext
"。
我可以想到各种 'hacky' 解决方法,比如只返回所有用户的列表,然后使用一些链式 Enum.map
函数 运行 通过它们并检查 fulltext
用于部分匹配,但如果有使用 Ecto 查询语法的更优雅的解决方案,我宁愿选择它。任何人都可以提供任何指导吗?
您可以在 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"]]
您可以使用 fragment
和 unnest
将数组转换为连接:
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)