Ecto написанный на Elixir DSL для коммуникации с базами данных. Ecto это не ORM. Почему? Да, потому что Elixir не объектно-ориентированный язык, вот и Ecto не может быть Object-Relational Mapping (объектно-реляционным отображением). Ecto — это абстракция над базами данных состоящая из нескольких больших модулей, которые позволяют создавать миграции, объявлять модели (схемы), добавлять и обновлять данные, а также посылать к ним запросы. Если вы знакомы с Rails, то для вас самой близкой аналогией, конечно же, будет его ORM ActiveRecord. Но эти две системы не являются копиями друг друга, и хороши в использовании в рамках своих базовых языков. На данный момент актуальная версия Ecto 2, она совместима с PostgreSQL и MySQL. Более ранняя версия дополнительно имеет совместимость с MSSQL, SQLite3 и MongoDB. Независимо от того, какая используется СУБД, формат функций Ecto будет всегда одинаковый. Также Ecto идёт из коробки с Phoenix и является хорошим стандартным решением. Если надумаете расширить брошюру, то милости прошу присоединиться к развитию данного репозитория https://github.com/wunsh/ecto-book-ru Как итог, вместо этого: Рекомендуется делать лучше так: Функция Ecto.Query.subquery/1 даёт возможность конвертировать любые запросы в подзапросы. Для примера, если вы хотите посчитать среднее кол-во просмотров публикаций, вы можете написать: Однако, если вы хотите посчитать средне кол-во просмотров только для 10 самых популярных постов, вам потребуется подзапрос: Для практического примера, если вы используете для подсчёта агрегированных данных функцию Repo.aggregate: В subquery/1 есть возможность задавать имена полям в подзапросах. Что позволит обрабатывать таблицы с конфликтующими именами: Функция Ecto.Repo.insert_all/3 предназначена для множественной вставки записей внутри одного запроса: Стоит учесть, что при вставки строк через insert_all/3 не обрабатываются автогенерируемые поля, такие как inserted_at или updated_at. Также insert_all/3 даёт возможность вставлять строки в базу данных в обход Ecto.Schema, просто указав имя таблицы: Теперь Ecto поддерживает many_to_many ассоциации: Значение в опции join_through может быть именем таблицы, которая содержит в себе колонки post_id и tag_id, или может быть схемой, такой как PostTag, которая содержит внешние ключи и автогенерируемые колонки. Отныне Ecto даёт возможность вставлять и изменять строки к belongs_to и many_to_many ассоциациям через changeset. Кроме этого, Ecto поддерживает определение ассоциаций напрямую в структуре данных для вставки. Например: Это улучшение позволяет проще вставлять древовидные структуры в базу данных. Функция Ecto.assoc/2 позволяет определять связи второго порядка, которые должны быть загружены к выбираемым записям. Как пример, можно получить авторов и комментарии к выбираемым публикациям: Лучше загружать связи через ассоциации, поскольку это не требует добавления полей к схеме выборки. Функции Ecto.Repo.insert/2 и Ecto.Repo.insert_all/3 начали поддерживать upserts (вставка и обновление) через опции :on_conflict и :conflict_target. Опция :on_conflict определяет, как база данных должна себя вести в случае совпадения primary key.
Опция :conflict_target определяет по каким полям необходимо проверять конфликты при вставке новых строк. В Ecto добавили выражения Ecto.Query.or_where/3 и Ecto.Query.or_having, которые добавляют новые фильтры к уже существующим условиям через "OR". Данная техника позволяет создавать выражения по крупицам, чтобы впоследствии интерполировать их в общий запрос. Для примера, у вас есть набор условий, из которых вы хотите построить свой запрос, но нужно выбрать только некоторые из них в зависимости от контекста: В примере выше показано, как можно построить запрос шаг за шагом, учитывая внешние условия, и в конце интерполировать всё вовнутрь одного запроса. Динамическое выражение всегда можно интерполировать внутри другого динамического выражения или внутри where, having, update или в условии on у join. Разбор документации модулей Ecto.Query и Ecto.Repo из HexDocs сделан на манер Интерфейса запросов ActiveRecord из RusRailsGuide. Текст ниже раскрывает различные способы получения данных из БД, используя Ecto. Примеры кода далее в тексте ниже будут относиться к некоторым из этих моделей: Для получения объектов из базы данных Ecto предоставляет несколько функций поиска. В каждую функцию поиска можно передавать аргументы для выполнения определенных запросов в базу данных без необходимости писать на чистом SQL. Ecto предусматривает два стиля построения запросов: с использованием ключевого слова и через выражение (функцию/макрос). Ниже перечислены некоторые выражения, предоставляемые Ecto, они объявлены в двух модулях: Ecto.Repo: Ecto.Query: Используя функцию get/3, можно получить запись, соответствующую определенному первичному ключу (primary key). Например: Функция get/3 возвратит nil, если ни одной записи не найдено. Если запрос не будет иметь primary key или их будет больше одного, то функция вызовет ошибку argument error. Функция get!/3 ведет себя подобно get/3, за исключением того, что она вызовет Ecto.NoResultsError, если не найдено ни одной соответствующей записи. Ближайший аналог данной функции в ActiveRecord это метод find. Используя функцию get_by/3, можно получить запись, соответствующую предоставленным условия выборки. Например: Функция get_by/3 возвратит nil, если ни одной записи не найдено. Функция get_by!/3 ведет себя подобно get_by/3, за исключением того, что она вызовет ошибку Ecto.NoResultsError, если не найдено ни одной соответствующей записи. Ближайший аналог данной функции в ActiveRecord это метод find_by_*. Используя функцию one/2, можно получить одну запись, соответствующую предоставленным условия выборки. Например: Функция one/2 возвратит nil, если ни одной записи не найдено. И функция вызовет ошибку если по запросу найдётся больше одной записи. Функция one!/2 ведет себя подобно one/2, за исключением того, что она вызовет ошибку Ecto.NoResultsError, если не найдено ни одной соответствующей записи. Ближайший аналог данной функции в ActiveRecord это метод first из <4 версии, который принимал условия выборки, а не лимит как сейчас. Используя функцию all/3, можно получить все записи, соответствующие предоставленным условиям запроса. Например: Функция all/3 вернет Ecto.QueryError, если запрос не пройдёт валидацию. Ближайший аналог данной функции в ActiveRecord это метод all из < 4 версии, который принимал условия выборки. Выражение where/3 позволяет определить условия для ограничения возвращаемых записей, которые представляет WHERE часть выражения SQL. В случае если передаётся несколько условий выборки, то они комбинируются оператором AND. Вызов по ключевому слову where: является неотъемлемой частью макроса from/2: Есть возможность интерполировать списки с условиями выборки, что позволяет предварительно собрать необходимые ограничения. Вызов макросом where/3: Выражение or_where/3 позволяет определить более гибкие условия для ограничения возвращаемых записей, которые представляет WHERE часть выражения SQL. Разница между where/3 и or_where/3 минимальна, но принципиальна. Переданное условие присоединяется к уже существующим через оператор OR. В случае если передаётся несколько условий выборки в or_where/3, то между собой они комбинируются оператором AND. Вызов по ключевому слову or_where: является неотъемлемой частью макроса from/2: Есть возможность интерполировать списки с условиями выборки, что позволяет предварительно собрать необходимые ограничения. Условия в списке между собой объединяются через AND, и присоединятся к существующим условиям через OR: … данное выражение эквивалентно: Вызов макросом or_where/3: Выражение order_by/3 позволяет определить условие сортировки записей полученных из базы данных. order_by/3 задаёт ORDER BY часть SQL запроса. Есть возможность сортировать сразу по нескольким полям. Направление сортировки по умолчанию на возрастание (:asc), может быть переопределено на убывание (:desc). Для каждого поля можно задать своё направление сортировки. Вызов по ключевому слову order_by: является неотъемлемой частью макроса from/2: Есть возможность интерполировать списки с полями сортировки, что позволяет предварительно собрать необходимые условия выборки. Вызов макросом order_by/3: Выражение select/3 позволяет определить поля таблиц, которые необходимо вернуть для получаемых записей из базы данных. select/3 задаёт SELECT часть SQL запроса. По умолчанию Ecto выбирает все множество полей результата, используя select *. Вызов по ключевому слову select: является неотъемлемой частью макроса from/2: Вызов макросом select/3: Важно: При ограничении полей выборки для ассоциаций важно выбирать внешние ключи связей, иначе Ecto не сможет найти связанные объекты. Для определения условия GROUP BY в SQL запросе, предназначен макрос group_by/3. Все столбцы упомянутые в SELECT, должны быть переданы в group_by/3. Это общее правило для агрегатных функций. Вызов по ключевому слову group_by: является неотъемлемой частью макроса from/2: Вызов макросом group_by/3: Для определения LIMIT в SQL запросе используйте выражение limit/3, оно определит количество необходимых записей, которые будут получены. Если limit/3 передан дважды, то первое значение будет перекрыто вторым. Для определения OFFSET в SQL запросе используйте выражение offset/3, оно определит количество записей, которые будут пропущены до начала возвращаемых записей. Если offset/3 передан дважды, то первое значение будет перекрыто вторым. Часто запросы обращаются к нескольким таблицам, такие запросы строятся с использованием конструкции JOIN. В Ecto для определения такой конструкции предназначено выражение join/5. По умолчанию стратегией присоединения таблицы является INNER JOIN, которую можно переопределить на: :inner, :left, :right, :cross or :full. В случае построения запроса по ключу, :join можно заменить на: :inner_join, :left_join, :right_join, :cross_join или :full_join. Вызов по ключевому слову join: является неотъемлемой частью макроса from/2: Все ключи переданные в on будут учтены как условия соединения. Есть возможность интерполировать правую часть относительно in. Для примера: Вызов макросом join/5: Ecto даёт возможность убрать уже определенные условия в запросе или вернуть значения по умолчанию, для этого используйте выражение exclude/2. Функция Ecto.Repo.insert_all/3 вставит все переданные записей. Функция insert_all/3 не обрабатывает автогенерируемые поля, такие как inserted_at или updated_at. Функция Ecto.Repo.update_all/3 обновит все строки подпавшие под условие запроса на переданные значения полей. Функция Ecto.Repo.delete_all/2 удаляет все строки, подпавшие под условие запроса. Операторы сравнения: ==, !=, <=, >=, <, >
Булевы операторы: and, or, not
Оператор включения: in/2
Функции поиска: like/2 и ilike/2
Проверка на null: is_nil/1
Агрегаторы: count/1, avg/1, sum/1, min/1, max/1
Функция для произвольных SQL подзапросов: fragment/1 Выполняет произвольный SQL запрос в рамках переданного репозитория. Ближайший аналог данной функции в ActiveRecord это метод find_by_sql. Конвертирует построенный из выражений запрос в SQL. Данная функция тёска аналогичного метода из ActiveRecord::Relation. http://guides.rubyonrails.org/active_record_querying.html https://hexdocs.pm/ecto/Ecto.html https://github.com/elixir-ecto/ecto https://blog.drewolson.org/composable-queries-ecto/ Learning with JB http://blog.plataformatec.com.br/2016/05/ectos-insert_all-and-schemaless-queries/ Если вам интересен функциональный язык программирования Elixir или вы просто сочувствующий то советую вам присоединиться к Telegram-чатам Wunsh && Elixir и ProElixir. У отечественного Elixir сообщества начинает появляться единая площадка в лице проекта Wunsh.ru. Сейчас у проекта есть тематическая рассылка, в которой нет ничего нелегального, раз в недельку будет приходить письмо с подборкой статей про Elixir на русском языке. Обновление от pure_evil — c MongoDB вторая версия Ecto работает, правда пока это в виде форка: https://github.com/michalmuskala/mongodb_ecto/pull/91 Программист