Запросы без схем
Исходный кодБольшинство запросов в Ecto записываются с использованием схем. Например, чтобы получить все записи из базы данных, можно написать:
MyApp.Repo.all(Post)
В приведённом выше примере Ecto знает все поля и их типы в схеме и преобразует приведённый запрос в следующий:
query =
from p in Post,
select: %Post{title: p.title, body: p.body, ...}
MyApp.Repo.all(query)
Хотя в большинстве запросов можно использовать схемы, Ecto также позволяет при необходимости писать обычные запросы без схем.
Например, можно выбрать все нужные поля без дублирования:
from "posts", select: [:title, :body]
Если задан список полей, Ecto автоматически преобразует его в карту или структуру.
Передавать список полей или ключевые списки можно почти во все конструкции запросов. Например, с помощью запроса на обновление можно изменить заголовок записи без схемы:
def update_title(post, new_title) do
query =
from "posts",
where: [id: ^post.id],
update: [set: [title: ^new_title]]
MyApp.Repo.update_all(query, [])
end
Конструкция Ecto.Query.update/3 поддерживает четыре команды:
-
:set— задаёт указанному столбцу заданные значения -
:inc— увеличивает значение указанного столбца на заданное значение -
:push— добавляет указанное значение в конец столбца-массива -
:pull— удаляет указанное значение из столбца-массива
Например, можно атомарно увеличить значение столбца с помощью команды :inc, как со схемами, так и без них:
def increment_page_views(post) do
query =
from "posts",
where: [id: ^post.id],
update: [inc: [page_views: 1]]
MyApp.Repo.update_all(query, [])
end
Рассмотрим ещё один пример. Представьте, что вы создаёте представление для отчётов. В этом случае может быть контрпродуктивно разбираться, как существующие схемы приложения соотносятся с создаваемым отчётом. Часто проще написать запрос, который возвращает только нужные данные, не пытаясь подогнать их под существующие схемы:
import Ecto.Query
def running_activities(start_at, end_at) do
query =
from u in "users",
join: a in "activities",
on: a.user_id == u.id,
where:
a.start_at > type(^start_at, :naive_datetime) and
a.end_at < type(^end_at, :naive_datetime),
group_by: a.user_id,
select: %{
user_id: a.user_id,
interval: a.end_at - a.start_at,
count: count(u.id)
}
MyApp.Repo.all(query)
end
Приведённая выше функция не использует схемы. Она возвращает только данные, необходимые для создания отчёта. Обратите внимание, что мы используем функцию type/2, чтобы указать ожидаемый тип интерполируемого аргумента и получить те же гарантии приведения типов, что и при использовании схемы.
Позволяя передавать обычные структуры данных в большинство операций с запросами, Ecto упрощает работу с запросами как со схемами, так и без них. Кроме того, это позволяет разработчикам писать динамические запросы, в которых поля, фильтры и сортировку нельзя задать заранее.
insert_all, update_all и delete_all
Ecto позволяет выполнять все операции с базой данных без схемы. Одна из доступных функций — Ecto.Repo.insert_all/3. С помощью insert_all разработчики могут за один раз вставить в репозиторий несколько записей, передав адаптеру напрямую источник данных и список полей со значениями:
MyApp.Repo.insert_all(
"posts",
[
[title: "hello", body: "world"],
[title: "another", body: "post"]
]
)
Обновлять и удалять записи также можно без схем с помощью Ecto.Repo.update_all/3 и Ecto.Repo.delete_all/2 соответственно:
# Use the ID to trigger updates
post = from p in "posts", where: [id: ^id]
# Update the title for all matching posts
{1, _} =
MyApp.Repo.update_all post, set: [title: "new title"]
# Delete all matching posts
{1, _} =
MyApp.Repo.delete_all post
Нетрудно заметить, что эти операции напрямую соответствуют своим вариантам в SQL и позволяют работать с базой данных без необходимости выполнять все операции через схемы.
© 2014 Chris McCord
Licensed under the MIT License.
https://ecto.hexdocs.pm/schemaless-queries.html