В есть раздел руководства Phoenix по контекстам, в котором добавляются функциональные возможности просмотра страниц в фиктивном контексте CMS. Функция, созданная в контексте CMS, выглядит следующим образом:
def inc_page_views(%Page{} = page) do
{1, [%Page{views: views}]} =
from(p in Page, where: p.id == ^page.id, select: [:views])
|> Repo.update_all(inc: [views: 1])
put_in(page.views, views)
end
Перефразируя, inc_page_views
принимает структуру Page
, использует ее id
для поиска соответствующей записи базы данных, использует Repo.update_all
для атомарного увеличения счетчика просмотров (см. документацию для примера чередования), гарантирует, что обновлена только 1 запись, и возвращает новый Page
с обновленным счетчиком просмотров.
Почему в этом примере используется Ecto.Repo.update_all/3
вместо Ecto.Repo.update/2
? Поскольку мы знаем, что хотим работать только с 1 записью, кажется странным потенциально обновлять кучу записей и задним числом проверять, что мы этого не сделали, вместо того, чтобы обновлять конкретный Ecto.Changeset
, который может выглядеть примерно так:
def inc_page_views(%Page{views: curr_views} = page) do
page
|> Page.changeset(%{views: curr_views + 1})
|> Repo.update()
end
Эта реализация короче / проще, но я предполагаю, что авторы документации Phoenix не использовали ее по уважительной причине. Я догадываюсь, что в версии Repo.update
должно отсутствовать свойство атомарного обновления, которое предположительно присутствует в версии Repo.update_all
, но я понятия не имею, почему! Может кто-нибудь помочь объяснить разницу между этими реализациями и почему документы могли выбрать первое?