如何为数据库查询实现分页?
How to implement pagination for a database query?
我是长生不老药编程的新手。我在 elixir 中遇到递归问题。
- 我有这样的块代码
def seen_page(page_id, total \ 0, type \ "ALL") do
repo = Shard.by_page(page_id)
sub_query =
if type === "ALL" do
from(c in Conversation, where: c.page_id == ^page_id and c.seen == false, limit: 500)
else
from(c in Conversation, where: c.page_id == ^page_id and c.seen == false and c.type == ^type, limit: 500)
end
from(c in Conversation, where: c.page_id == ^page_id, join: s in subquery(sub_query), on: s.id == c.id)
|> repo.update_all([set: [seen: true, unread_count: 0]])
|> case do
{count, nil} ->
case count do
500 ->
seen_page(page_id, total + 500, type)
count ->
IO.puts "Đã đánh dấu tất cả #{type} của trang #{page_id} thành đã đọc"
Cache.update_page_unread_count_limit_2k(page_id)
total + count
end
_ ->
false
end
end
- 比如我有2000条记录,我每次取500条记录直到结束return到前端
- 现在我想每次获取500条记录到return前端。然后继续取剩下的记录。有什么办法吗?
- 如果有人能帮助我,我将不胜感激。
首先,了解网络请求应该快速收到响应。响应时间过长的网站可能会给访问者带来糟糕的体验——即使是适度缓慢的响应也会导致访问者彻底离开。我手边没有 link,但我记得看到一篇文章表明,如果事情没有发生,访问者将在大约 3 秒内离开页面。如果响应花费的时间太长,也有可能完全超时。简而言之,反应慢是不好的——它们甚至在某些 web attacks.
中发挥了作用
所以你应该永远不要将你的长运行进程直接连接到网页中。如果您需要触发花费超过几百毫秒的时间,您应该始终异步 处理它,这样访问者就不会等待。您只需快速告诉您的访问者“好的,我们正在努力”并完成。
在 Elixir 中执行此操作的一种简单方法是通过 Task.start/3
。例如,如果你的慢任务是 Foo
模块中的 :slow_thing
函数,你可以像这样异步调用它:
result = Task.start(Foo, :slow_thing, ["Hello..."])
IO.inspect(result)
请注意,发送邮件会立即。 result
类似于 {:ok, #PID<0.99.0>}
—— 它对 Foo.slow_thing/1
函数的作用或可能需要多长时间的了解为零。它知道模块和函数存在,但仅此而已。
例如,如果您异步调用的模块和函数如下所示:
defmodule Foo do
def slow_thing(input) do
Process.sleep(5_000) # <-- simulated long-running thing
File.write("side_effect.log","#{input}\n", [:utf8, :append])
end
end
然后您可以检查预期的副作用(在本例中查看日志文件)。请记住,调用 Task.start/3
的过程必须仍然是 运行 才能使 Foo.slow_thing/1
完成。如果您在 Phoenix 应用程序中执行此操作,这应该不是问题,但如果您试图在一个简单的 .exs
脚本中执行此操作,请记住,一旦脚本完成,启动的任务将关闭。
既然你问的是如何跟踪进度,事情就有点复杂了。认真地问问自己,这是一项要求还是只是一件幸事 -- 它可能不值得付出额外的工作。
解决这类问题的诀窍是递归:一个调用自身的函数。它仍然应该被异步调用,但在每次迭代期间它可以调用一个您可以观察到的副作用。考虑这样的事情,它显示了用于对 select 查询进行分页的递归:
def update_conversations(page_id, offset \ 0) do
from(c in Conversation, where: c.page_id == ^page_id and c.seen == false, limit: 500, offset: ^offset)
|> Repo.all()
|> case do
[] ->
# another side-effect somewhere to indicate completion
SomeSideEffect.done(page_id)
:done
results ->
# side-effect somewhere to indicate progress
SomeSideEffect.progress(page_id, 500)
update_conversations(page_id, offset + 500) # <-- recursion!
end
end
您可以使更新查询适应这种类型的模式,但重要的是您要选择有用的副作用。网页可以读取“进度缓存”的值,每次访问者刷新页面时,他们都可以看到进度。如果您不想强制刷新,您可以 Javascript 定期查询端点或使用 LiveView 将更新推送给访问者。
我是长生不老药编程的新手。我在 elixir 中遇到递归问题。
- 我有这样的块代码
def seen_page(page_id, total \ 0, type \ "ALL") do
repo = Shard.by_page(page_id)
sub_query =
if type === "ALL" do
from(c in Conversation, where: c.page_id == ^page_id and c.seen == false, limit: 500)
else
from(c in Conversation, where: c.page_id == ^page_id and c.seen == false and c.type == ^type, limit: 500)
end
from(c in Conversation, where: c.page_id == ^page_id, join: s in subquery(sub_query), on: s.id == c.id)
|> repo.update_all([set: [seen: true, unread_count: 0]])
|> case do
{count, nil} ->
case count do
500 ->
seen_page(page_id, total + 500, type)
count ->
IO.puts "Đã đánh dấu tất cả #{type} của trang #{page_id} thành đã đọc"
Cache.update_page_unread_count_limit_2k(page_id)
total + count
end
_ ->
false
end
end
- 比如我有2000条记录,我每次取500条记录直到结束return到前端
- 现在我想每次获取500条记录到return前端。然后继续取剩下的记录。有什么办法吗?
- 如果有人能帮助我,我将不胜感激。
首先,了解网络请求应该快速收到响应。响应时间过长的网站可能会给访问者带来糟糕的体验——即使是适度缓慢的响应也会导致访问者彻底离开。我手边没有 link,但我记得看到一篇文章表明,如果事情没有发生,访问者将在大约 3 秒内离开页面。如果响应花费的时间太长,也有可能完全超时。简而言之,反应慢是不好的——它们甚至在某些 web attacks.
中发挥了作用所以你应该永远不要将你的长运行进程直接连接到网页中。如果您需要触发花费超过几百毫秒的时间,您应该始终异步 处理它,这样访问者就不会等待。您只需快速告诉您的访问者“好的,我们正在努力”并完成。
在 Elixir 中执行此操作的一种简单方法是通过 Task.start/3
。例如,如果你的慢任务是 Foo
模块中的 :slow_thing
函数,你可以像这样异步调用它:
result = Task.start(Foo, :slow_thing, ["Hello..."])
IO.inspect(result)
请注意,发送邮件会立即。 result
类似于 {:ok, #PID<0.99.0>}
—— 它对 Foo.slow_thing/1
函数的作用或可能需要多长时间的了解为零。它知道模块和函数存在,但仅此而已。
例如,如果您异步调用的模块和函数如下所示:
defmodule Foo do
def slow_thing(input) do
Process.sleep(5_000) # <-- simulated long-running thing
File.write("side_effect.log","#{input}\n", [:utf8, :append])
end
end
然后您可以检查预期的副作用(在本例中查看日志文件)。请记住,调用 Task.start/3
的过程必须仍然是 运行 才能使 Foo.slow_thing/1
完成。如果您在 Phoenix 应用程序中执行此操作,这应该不是问题,但如果您试图在一个简单的 .exs
脚本中执行此操作,请记住,一旦脚本完成,启动的任务将关闭。
既然你问的是如何跟踪进度,事情就有点复杂了。认真地问问自己,这是一项要求还是只是一件幸事 -- 它可能不值得付出额外的工作。
解决这类问题的诀窍是递归:一个调用自身的函数。它仍然应该被异步调用,但在每次迭代期间它可以调用一个您可以观察到的副作用。考虑这样的事情,它显示了用于对 select 查询进行分页的递归:
def update_conversations(page_id, offset \ 0) do
from(c in Conversation, where: c.page_id == ^page_id and c.seen == false, limit: 500, offset: ^offset)
|> Repo.all()
|> case do
[] ->
# another side-effect somewhere to indicate completion
SomeSideEffect.done(page_id)
:done
results ->
# side-effect somewhere to indicate progress
SomeSideEffect.progress(page_id, 500)
update_conversations(page_id, offset + 500) # <-- recursion!
end
end
您可以使更新查询适应这种类型的模式,但重要的是您要选择有用的副作用。网页可以读取“进度缓存”的值,每次访问者刷新页面时,他们都可以看到进度。如果您不想强制刷新,您可以 Javascript 定期查询端点或使用 LiveView 将更新推送给访问者。