如何实现数据库查询分页?



我是elixir编程的新手。我在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条记录,直到结束并将其返回到前端
  • 现在我想每次得到500条记录来返回前端。然后继续做剩下的记录。有什么办法吗?
  • 如果有人能帮助我,我将非常感激。

首先,要明白web请求应该快速收到响应。响应时间过长的网站可能会给访问者带来糟糕的体验——即使是适度缓慢的响应也会导致访问者彻底离开。我没有现成的链接,但我记得看到一篇文章表明,如果没有事情发生,访问者会在3秒内离开页面。如果响应时间太长,也可能会完全超时。简而言之,缓慢的响应是不好的——它们甚至在一些网络攻击中起作用。

所以你应该不要将长时间运行的流程直接连接到web页面中。如果你需要触发一些超过几百毫秒的事情,你应该总是异步地处理它,这样访问者就不用等待了。你只需快速告诉访问者"好的,我们正在努力"。

在《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脚本中使这个工作,只要记住,一旦脚本完成,启动的任务将关闭。

既然你的问题是关于如何跟踪进度,事情就有点复杂了。认真问问自己如果这是一个要求或者只是可有可无——可能不是值得的额外工作

解决这类问题的技巧是递归:一个调用自己的函数。它仍然应该异步调用,但在每次迭代期间,它可以调用您可以观察到的副作用。考虑下面的代码,它显示了用于为选择查询分页的递归:
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向访问者推送更新。

相关内容

  • 没有找到相关文章

最新更新