SlideShare a Scribd company logo
Ecto DSL
Domain specific language for writing queries and interacting with
databases in Elixir.
Yurii Bodarev
Back-end software developer
twitter.com/bodarev_yurii
github.com/yuriibodarev
Ecto official resources
github.com/elixir-ecto/ecto
hexdocs.pm/ecto/Ecto.html
pages.plataformatec.com.br/ebook-whats-new-in-ecto-2-0
Brief contents
• Elixir Data Structures
• Basic data types and structures
• Associative data structures
• Structs
• Pattern Matching
• Ecto
• Ecto.Repo
• Ecto.Schema
• Ecto.Changeset
• Ecto.Query
Basic data types and structures
• Atoms
• Lists
• Tuples
Atoms
Atoms are constants where their name is their own value.
iex> :hello
:hello
iex> :hello == :world
false
iex> true == :true
true
Lists (Linked)
Lists are used to manage dynamic, variable-sized collections of data of
any type.
iex> [1, "abc", true, 3]
[1, "abc", true, 3]
iex> length([1, 2, 3])
3
List - recursive structure
[head | tail]
iex> [1 | [2, 3, 4]]
[1, 2, 3, 4]
[head | [head | [head | tail…]]]
iex> [1 | [2 | [3 | [4 | []]]]]
[1, 2, 3, 4]
List - recursive structure
iex> hd([1, 2, 3, 4])
1
iex> tl([1, 2, 3, 4])
[2, 3, 4]
iex> tl([2, 3, 4])
[3, 4]
iex> tl([4])
[]
Tuples
Tuples are untyped structures often used to group a fixed number of elements
together.
iex> tuple = {:ok, "hello"}
{:ok, "hello"}
iex> elem(tuple, 1)
"hello"
iex> tuple_size(tuple)
2
Associative data structures
• Keyword lists
• Maps
List as key-value data structure
It is common to use a list of 2-item tuples as the representation of a
key-value data structure
iex> list = [{"a", 1}, {"b", 2}, {"c", 3}]
[{"a", 1}, {"b", 2}, {"c", 3}]
iex> List.keyfind(list, "b", 0)
{"b", 2}
Keyword lists
When we have a list of tuples and the first item of the tuple (i.e. the
key) is an atom, we call it a keyword list.
iex> [{:a, 1}, {:b, 2}, {:c, 3}]
[a: 1, b: 2, c: 3]
Keyword lists
Elixir supports a special syntax for defining such lists: [key: value]
iex> [a: 1, b: 2, c: 3] == [{:a, 1}, {:b, 2}, {:c, 3}]
true
• Keys must be atoms.
• Keys are ordered, as specified by the developer.
• Keys can be given more than once.
We can use all operations available to lists on keyword lists
iex> list = [a: 1, c: 3, b: 2]
[a: 1, c: 3, b: 2]
iex> hd(list)
{:a, 1}
iex> tl(list)
[c: 3, b: 2]
iex> list[:a]
1
iex> newlist = [a: 0] ++ list
[a: 0, a: 1, c: 3, b: 2]
iex> newlist[:a]
0
iex> list[:d]
nil
Keyword lists - default mechanism for passing options to
functions in Elixir
iex> if true, do: "THIS"
"THIS"
iex> if false, do: "THIS", else: "THAT"
"THAT"
iex> if(false, [do: "THIS", else: "THAT"])
"THAT"
When the keyword list is the last argument of a function, the square brackets
are optional.
Example of the Ecto query
query = from w in Weather,
where: w.prcp > 0,
where: w.temp < 20,
select: w
Maps
A map is a key-value store, where keys and values can be any term. A map is created using
the %{} syntax. Maps’ keys do not follow developer ordering.
iex> map = %{:a => 1, 2 => "b", "c" => 3}
%{2 => "b", :a => 1, "c" => 3}
iex> map[:a]
1
iex> map[2]
"b"
iex> map["d"]
nil
Maps
When all the keys in a map are atoms, you can use the keyword syntax.
iex> map = %{a: 1, b: 2, c: 3}
%{a: 1, b: 2, c: 3}
iex> map.a
1
iex> map.d
** (KeyError) key :d not found in: %{a: 1, b: 2, c: 3}
iex> %{map | c: 5}
%{a: 1, b: 2, c: 5}
iex> %{map | d: 0}
** (KeyError) key :d not found in: %{a: 1, b: 2, c: 3}
Structs
Structs are extensions built on top of maps that provide compile-time
checks and default values.
iex> defmodule User do
...> defstruct name: "Ivan", age: 25
...> end
iex> %User{}
%User{age: 25, name: "Ivan"}
Structs
iex> %User{name: "Maria"}
%User{age: 25, name: "Maria"}
iex> %User{other: "Something"}
** (KeyError) key :other not found in: %User{age:
25, name: "Ivan"}
Structs are bare maps underneath, but none of the protocols
implemented for maps are available for structs
iex> ivan = %User{}
%User{age: 25, name: "Ivan"}
iex> is_map(ivan)
true
iex> Map.keys(ivan)
[:__struct__, :age, :name]
iex> ivan.__struct__
User
iex> ivan[:age]
** (UndefinedFunctionError)
function User.fetch/2 is
undefined (User does not
implement the Access behaviour)
Pattern matching
iex> {a, b, c} = {:hello, "world", 42}
{:hello, "world", 42}
iex> a
:hello
iex> b
"world"
iex> c
42
A pattern match will error if the sides can’t be
matched
iex> {a, b, c} = {:hello, "world"}
** (MatchError) no match of right hand side value:
{:hello, "world"}
iex> {a, b, c} = [:hello, "world", 42]
** (MatchError) no match of right hand side value:
[:hello, "world", 42]
We can match on specific values
iex> {:ok, result} = {:ok, 13}
{:ok, 13}
iex> result
13
iex> {:ok, result} = {:error, "Not Found!"}
** (MatchError) no match of right hand side value:
{:error, "Not Found!"}
We can match on specific values
post = Repo.get!(Post, 42)
case Repo.delete post do
{:ok, struct} -> # Deleted with success
{:error, changeset} -> # Something went wrong
end
Pattern match on lists
iex> [a, b, c] = [1, 2, 3]
[1, 2, 3]
iex> b
2
iex> [head | tail] = [1, 2, 3]
[1, 2, 3]
iex> head
1
iex> tail
[2, 3]
iex> [] = [1, 2, 3]
** (MatchError) no match of
right hand side value: [1, 2, 3]
Pattern match on keyword lists
iex> [a: a] = [a: 1]
[a: 1]
iex> [a: a] = [a: 1, b: 2]
** (MatchError) no match of right hand side value: [a: 1, b: 2]
iex> [b: b, a: a] = [a: 1, b: 2]
** (MatchError) no match of right hand side value: [a: 1, b: 2]
Pattern match on maps
iex> %{} = %{a: 1, b: 2}
%{a: 1, b: 2}
iex> %{b: b} = %{a: 1, b: 2}
%{a: 1, b: 2}
iex> b
2
iex> %{c: c} = %{a: 1, b: 2}
** (MatchError) no match of right hand side value: %{a: 1, b: 2}
The pin ^ operator and _
iex> x = 2
2
iex> {1, ^x} = {1, 2}
{1, 2}
iex> {a, _} = {1, 2}
{1, 2}
iex> a
1
Ecto
Ecto is split into 4 main components:
• Ecto.Repo - repositories are wrappers around the data store.
• Ecto.Schema - schemas are used to map any data source into an Elixir
struct.
• Ecto.Changeset - allow developers to filter, cast, and validate changes
before we apply them to the data.
• Ecto.Query - written in Elixir syntax, queries are used to retrieve
information from a given repository.
Ecto playground
Ecto in not an ORM
github.com/yuriibodarev/Ecto_not_ORM
Requires: PostgreSQL
Run within IEx console: iex -S mix
Repositories
Via the repository, we can create, update, destroy and query existing
database entries.
Repositories
Ecto.Repo is a wrapper around the database. We can define a
repository as follows (libblogrepo.ex):
defmodule Blog.Repo do
use Ecto.Repo, otp_app: :blog
end
Repositories
A repository needs an adapter and credentials to communicate to the database.
Configuration for the Repo usually defined in your config/config.exs:
config :blog, Blog.Repo,
adapter: Ecto.Adapters.Postgres,
database: "blog_repo",
username: "postgres",
password: "postgres",
hostname: "localhost"
Repositories
Each repository in Ecto defines a start_link/0. Usually this function is invoked as part
of your application supervision tree (libblog.ex):
def start(_type, _args) do
import Supervisor.Spec, warn: false
children = [ worker(Blog.Repo, []), ]
opts = [strategy: :one_for_one, name: Blog.Supervisor]
Supervisor.start_link(children, opts)
end
Schema
Schemas allows developers to define the shape of their data. (libbloguser.ex)
defmodule Blog.User do
use Ecto.Schema
schema "users" do
field :name, :string
field :reputation, :integer, default: 0
has_many :posts, Blog.Post, on_delete: :delete_all
timestamps
end
end
Schema
By defining a schema, Ecto automatically defines a struct:
iex> user = %Blog.User{name: "Bill"}
%Blog.User{__meta__: #Ecto.Schema.Metadata<:built,
"users">, id: nil, inserted_at: nil, name: "Bill"},
posts: #Ecto.Association.NotLoaded<association
:posts is not loaded>, reputation: 0, updated_at:
nil}
Schema
Using Schema we can interact with a repository:
iex> user = %Blog.User{name: "Bill", reputation: 10}
%Blog.User{…}
iex> Blog.Repo.insert!(user)
%Blog.User{__meta__: #Ecto.Schema.Metadata<:loaded,
"users">, id: 6, inserted_at: ~N[2016-12-13
16:16:35.983000], name: "Bill", posts:
#Ecto.Association.NotLoaded<association :posts is not
loaded>, reputation: 10, updated_at: ~N[2016-12-13
16:16:36.001000]}
Schema
# Get the user back
iex> newuser = Blog.Repo.get(Blog.User, 6)
iex> newuser.id
6
# Delete it
iex> Blog.Repo.delete(newuser)
{:ok, %Blog.User{…, id: 6,…}}
Schema
We can use pattern matching on Structs created with Schemas:
iex> %{name: name, reputation: reputation} =
...> Blog.Repo.get(Blog.User, 1)
iex> name
"Alex"
iex> reputation
144
Changesets
We can add changesets to our schemas to validate changes before we
apply them to the data (libbloguser.ex):
def changeset(user, params  %{}) do
user
|> cast(params, [:name, :reputation])
|> validate_required([:name, :reputation])
|> validate_inclusion(:reputation, -999..999)
end
Changesets
iex> alina = %Blog.User{name: "Alina"}
iex> correct_changeset = Blog.User.changeset(alina, %{reputation: 55})
#Ecto.Changeset<action: nil, changes: %{reputation: 55}, errors: [],
data: #Blog.User<>, valid?: true>
iex> invalid_changeset = Blog.User.changeset(alina, %{reputation: 1055})
#Ecto.Changeset<action: nil, changes: %{reputation: 1055}, errors:
[reputation: {"is invalid", [validation: :inclusion]}], data:
#Blog.User<>, valid?: false>
Changeset with Repository functions
iex> valid_changeset.valid?
true
iex> Blog.Repo.insert(valid_changeset)
{:ok, %Blog.User{…, id: 7, …}}
Changeset with Repository functions
iex> invalid_changeset.valid?
false
iex> Blog.Repo.insert(invalid_changeset)
{:error, #Ecto.Changeset<action: :insert, changes:
%{reputation: 1055}, errors: [reputation: {"is
invalid", [validation: :inclusion]}], data:
#Blog.User<>, valid?: false>}
Changeset with Repository functions
case Blog.Repo.update(changeset) do
{:ok, user} ->
# user updated
{:error, changeset} ->
# an error occurred
end
We can provide different changeset functions
for different use cases
def registration_changeset(user, params) do
# Changeset on create
end
def update_changeset(user, params) do
# Changeset on update
end
Query
Ecto allows you to write queries in Elixir and send them to the
repository, which translates them to the underlying database.
Query using predefined Schema
# Query using predefined Schema
query = from u in User,
where: u.reputation > 35,
select: u
# Returns %User{} structs matching the query
Repo.all(query)
[%Blog.User{…, id: 2, …, name: "Bender", …, reputation: 42, …},
%Blog.User{…, id: 1, …, name: "Alex", …, reputation: 144, …}]
Directly querying the “users” table
# Directly querying the “users” table
query = from u in "users",
where: u.reputation > 30,
select: %{name: u.name, reputation: u.reputation}
# Returns maps as defined in select
Repo.all(query)
[%{name: "Bender", reputation: 42}, %{name: "Alex", reputation: 144}]
External values in Queries
# ^ operator
min = 33
query = from u in "users",
where: u.reputation > ^min,
select: u.name
# casting
mins = "33"
query = from u in "users",
where: u.reputation > type(^mins, :integer),
select: u.name
External values in Queries
If query is created with predefined Schema than Ecto
will automatically cast external value
min = "35"
Repo.all(from u in User, where: u.reputation > ^min)
You can also skip Select to retrieve all fields specified in the Schema
Associations
schema "users" do
field :name, :string
field :reputation, :integer, default: 0
has_many :posts, Blog.Post, on_delete: :delete_all
timestamps
end
Associations
alex = Repo.get_by(User, name: "Alex")
%Blog.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">, id: 1,
inserted_at: ~N[2016-12-17 06:36:54.916000], name: "Alex",
posts: #Ecto.Association.NotLoaded<association :posts is not loaded>,
reputation: 13, updated_at: ~N[2016-12-17 06:36:54.923000]}
Associations
alex_wposts = Repo.preload(alex, :posts)
%Blog.User{…, id: 1, name: "Alex",
posts: [%Blog.Post{__meta__: #Ecto.Schema.Metadata<:loaded, "posts">,
body: "World",
comments: #Ecto.Association.NotLoaded<association :comments is not loaded>,
id: 1, inserted_at: ~N[2016-12-17 06:36:54.968000], pinned: true,
title: "Hello", updated_at: ~N[2016-12-17 06:36:54.968000],
user: #Ecto.Association.NotLoaded<association :user is not loaded>,
user_id: 1}], …}
Associations
users_wposts = from u in User,
where: u.reputation > 10,
order_by: [asc: u.name],
preload: [:posts]
Queries Composition
alex = Repo.get_by(User, name: "Alex")
alex_post = from p in Post,
where: p.user_id == ^alex.id
alex_pin = from ap in alex_post,
where: ap.pinned == true
Fragments
title = "hello"
from(p in Post,
where: fragment("lower(?)", p.title) == ^title)
|> Repo.all()
THANK YOU!!!

More Related Content

PPTX
Java Foundations: Maps, Lambda and Stream API
Svetlin Nakov
 
PPTX
Java Foundations: Lists, ArrayList<T>
Svetlin Nakov
 
PPTX
Introduction to python programming 1
Giovanni Della Lunga
 
PDF
Arrays in python
Lifna C.S
 
PPTX
37c
Sireesh K
 
PDF
Basic data structures in python
Lifna C.S
 
PDF
Elixir in a nutshell - Fundamental Concepts
Héla Ben Khalfallah
 
PPTX
Introduction to python programming 2
Giovanni Della Lunga
 
Java Foundations: Maps, Lambda and Stream API
Svetlin Nakov
 
Java Foundations: Lists, ArrayList<T>
Svetlin Nakov
 
Introduction to python programming 1
Giovanni Della Lunga
 
Arrays in python
Lifna C.S
 
Basic data structures in python
Lifna C.S
 
Elixir in a nutshell - Fundamental Concepts
Héla Ben Khalfallah
 
Introduction to python programming 2
Giovanni Della Lunga
 

What's hot (19)

PDF
1. python
PRASHANT OJHA
 
PDF
Where's My SQL? Designing Databases with ActiveRecord Migrations
Eleanor McHugh
 
PPTX
Python Modules and Libraries
Venugopalavarma Raja
 
PDF
Magicke metody v Pythonu
Jirka Vejrazka
 
PDF
Introduction to python programming
Rakotoarison Louis Frederick
 
PDF
Python programming : Arrays
Emertxe Information Technologies Pvt Ltd
 
PDF
Functional es6
Natalia Zaslavskaya
 
PPTX
Ggplot2 v3
Josh Doyle
 
PDF
Swift에서 꼬리재귀 사용기 (Tail Recursion)
진성 오
 
PPT
Python tutorialfeb152012
Shani729
 
PDF
Python Workshop Part 2. LUG Maniapl
Ankur Shrivastava
 
PPTX
Farhana shaikh webinar_dictionaries
Farhana Shaikh
 
PDF
Python dictionary : past, present, future
delimitry
 
PPT
Functional Programming
Olexandra Dmytrenko
 
PPT
Python tutorial
Rajiv Risi
 
PDF
Begin with Python
Narong Intiruk
 
PDF
Closure, Higher-order function in Swift
SeongGyu Jo
 
PDF
Monads and Monoids by Oleksiy Dyagilev
JavaDayUA
 
PPTX
Python programming Part -6
Megha V
 
1. python
PRASHANT OJHA
 
Where's My SQL? Designing Databases with ActiveRecord Migrations
Eleanor McHugh
 
Python Modules and Libraries
Venugopalavarma Raja
 
Magicke metody v Pythonu
Jirka Vejrazka
 
Introduction to python programming
Rakotoarison Louis Frederick
 
Python programming : Arrays
Emertxe Information Technologies Pvt Ltd
 
Functional es6
Natalia Zaslavskaya
 
Ggplot2 v3
Josh Doyle
 
Swift에서 꼬리재귀 사용기 (Tail Recursion)
진성 오
 
Python tutorialfeb152012
Shani729
 
Python Workshop Part 2. LUG Maniapl
Ankur Shrivastava
 
Farhana shaikh webinar_dictionaries
Farhana Shaikh
 
Python dictionary : past, present, future
delimitry
 
Functional Programming
Olexandra Dmytrenko
 
Python tutorial
Rajiv Risi
 
Begin with Python
Narong Intiruk
 
Closure, Higher-order function in Swift
SeongGyu Jo
 
Monads and Monoids by Oleksiy Dyagilev
JavaDayUA
 
Python programming Part -6
Megha V
 
Ad

Similar to Ecto DSL Introduction - Yurii Bodarev (20)

PDF
Introducción a Elixir
Svet Ivantchev
 
PDF
Python Programming: Lists, Modules, Exceptions
Sreedhar Chowdam
 
PDF
Introduction to Elixir
brien_wankel
 
PDF
Arrays and function basic c programming notes
GOKULKANNANMMECLECTC
 
PDF
MATLAB Programming
محمدعبد الحى
 
PDF
An overview of Python 2.7
decoupled
 
PDF
A tour of Python
Aleksandar Veselinovic
 
PPTX
Python list tuple dictionary .pptx
miteshchaudhari4466
 
PDF
Matlab solved problems
Make Mannan
 
PPTX
Matlab-1.pptx
aboma2hawi
 
PDF
Basic R Data Manipulation
Chu An
 
PDF
Introduction to Elixir
Diacode
 
PPTX
Using-Python-Libraries.9485146.powerpoint.pptx
UadAccount
 
PPTX
Python Interview Questions | Python Interview Questions And Answers | Python ...
Simplilearn
 
PDF
ClojurianからみたElixir
Kent Ohashi
 
PPT
R workshop
Revanth19921
 
PPTX
C++ process new
敬倫 林
 
PPTX
scripting in Python
Team-VLSI-ITMU
 
PPTX
Chp 3 C++ for newbies, learn fast and earn fast
nhbinaaa112
 
PPT
ComandosDePython_ComponentesBasicosImpl.ppt
oscarJulianPerdomoCh1
 
Introducción a Elixir
Svet Ivantchev
 
Python Programming: Lists, Modules, Exceptions
Sreedhar Chowdam
 
Introduction to Elixir
brien_wankel
 
Arrays and function basic c programming notes
GOKULKANNANMMECLECTC
 
MATLAB Programming
محمدعبد الحى
 
An overview of Python 2.7
decoupled
 
A tour of Python
Aleksandar Veselinovic
 
Python list tuple dictionary .pptx
miteshchaudhari4466
 
Matlab solved problems
Make Mannan
 
Matlab-1.pptx
aboma2hawi
 
Basic R Data Manipulation
Chu An
 
Introduction to Elixir
Diacode
 
Using-Python-Libraries.9485146.powerpoint.pptx
UadAccount
 
Python Interview Questions | Python Interview Questions And Answers | Python ...
Simplilearn
 
ClojurianからみたElixir
Kent Ohashi
 
R workshop
Revanth19921
 
C++ process new
敬倫 林
 
scripting in Python
Team-VLSI-ITMU
 
Chp 3 C++ for newbies, learn fast and earn fast
nhbinaaa112
 
ComandosDePython_ComponentesBasicosImpl.ppt
oscarJulianPerdomoCh1
 
Ad

More from Elixir Club (20)

PDF
Kubernetes + Docker + Elixir - Alexei Sholik, Andrew Dryga | Elixir Club Ukraine
Elixir Club
 
PDF
Integrating 3rd parties with Ecto - Eduardo Aguilera | Elixir Club Ukraine
Elixir Club
 
PDF
— An async template - Oleksandr Khokhlov | Elixir Club Ukraine
Elixir Club
 
PDF
BEAM architecture handbook - Andrea Leopardi | Elixir Club Ukraine
Elixir Club
 
PDF
You ain't gonna need write a GenServer - Ulisses Almeida | Elixir Club Ukraine
Elixir Club
 
PDF
— Knock, knock — An async templates — Who’s there? - Alexander Khokhlov | ...
Elixir Club
 
PDF
Performance measurement methodology — Maksym Pugach | Elixir Evening Club 3
Elixir Club
 
PDF
Erlang cluster. How is it? Production experience. — Valerii Vasylkov | Elixi...
Elixir Club
 
PDF
Promo Phx4RailsDevs - Volodya Sveredyuk
Elixir Club
 
PDF
Web of today — Alexander Khokhlov
Elixir Club
 
PDF
ElixirConf Eu 2018, what was it like? – Eugene Pirogov
Elixir Club
 
PDF
Implementing GraphQL API in Elixir – Victor Deryagin
Elixir Club
 
PDF
WebPerformance: Why and How? – Stefan Wintermeyer
Elixir Club
 
PDF
GenServer in Action – Yurii Bodarev
Elixir Club
 
PDF
Russian Doll Paradox: Elixir Web without Phoenix - Alex Rozumii
Elixir Club
 
PDF
Practical Fault Tolerance in Elixir - Alexei Sholik
Elixir Club
 
PDF
Phoenix and beyond: Things we do with Elixir - Alexander Khokhlov
Elixir Club
 
PDF
Monads are just monoids in the category of endofunctors - Ike Kurghinyan
Elixir Club
 
PDF
Craft effective API with GraphQL and Absinthe - Ihor Katkov
Elixir Club
 
PDF
Elixir in a service of government - Alex Troush
Elixir Club
 
Kubernetes + Docker + Elixir - Alexei Sholik, Andrew Dryga | Elixir Club Ukraine
Elixir Club
 
Integrating 3rd parties with Ecto - Eduardo Aguilera | Elixir Club Ukraine
Elixir Club
 
— An async template - Oleksandr Khokhlov | Elixir Club Ukraine
Elixir Club
 
BEAM architecture handbook - Andrea Leopardi | Elixir Club Ukraine
Elixir Club
 
You ain't gonna need write a GenServer - Ulisses Almeida | Elixir Club Ukraine
Elixir Club
 
— Knock, knock — An async templates — Who’s there? - Alexander Khokhlov | ...
Elixir Club
 
Performance measurement methodology — Maksym Pugach | Elixir Evening Club 3
Elixir Club
 
Erlang cluster. How is it? Production experience. — Valerii Vasylkov | Elixi...
Elixir Club
 
Promo Phx4RailsDevs - Volodya Sveredyuk
Elixir Club
 
Web of today — Alexander Khokhlov
Elixir Club
 
ElixirConf Eu 2018, what was it like? – Eugene Pirogov
Elixir Club
 
Implementing GraphQL API in Elixir – Victor Deryagin
Elixir Club
 
WebPerformance: Why and How? – Stefan Wintermeyer
Elixir Club
 
GenServer in Action – Yurii Bodarev
Elixir Club
 
Russian Doll Paradox: Elixir Web without Phoenix - Alex Rozumii
Elixir Club
 
Practical Fault Tolerance in Elixir - Alexei Sholik
Elixir Club
 
Phoenix and beyond: Things we do with Elixir - Alexander Khokhlov
Elixir Club
 
Monads are just monoids in the category of endofunctors - Ike Kurghinyan
Elixir Club
 
Craft effective API with GraphQL and Absinthe - Ihor Katkov
Elixir Club
 
Elixir in a service of government - Alex Troush
Elixir Club
 

Recently uploaded (20)

PDF
Key Features to Look for in Arizona App Development Services
Net-Craft.com
 
PDF
Enhancing Healthcare RPM Platforms with Contextual AI Integration
Cadabra Studio
 
PPTX
TRAVEL APIs | WHITE LABEL TRAVEL API | TOP TRAVEL APIs
philipnathen82
 
PDF
WatchTraderHub - Watch Dealer software with inventory management and multi-ch...
WatchDealer Pavel
 
PPTX
Presentation about variables and constant.pptx
safalsingh810
 
PPTX
Maximizing Revenue with Marketo Measure: A Deep Dive into Multi-Touch Attribu...
bbedford2
 
PPT
Activate_Methodology_Summary presentatio
annapureddyn
 
PDF
Balancing Resource Capacity and Workloads with OnePlan – Avoid Overloading Te...
OnePlan Solutions
 
PDF
New Download FL Studio Crack Full Version [Latest 2025]
imang66g
 
PDF
49784907924775488180_LRN2959_Data_Pump_23ai.pdf
Abilash868456
 
PPTX
slidesgo-unlocking-the-code-the-dynamic-dance-of-variables-and-constants-2024...
kr2589474
 
PDF
Using licensed Data Loss Prevention (DLP) as a strategic proactive data secur...
Q-Advise
 
PDF
New Download MiniTool Partition Wizard Crack Latest Version 2025
imang66g
 
PDF
vAdobe Premiere Pro 2025 (v25.2.3.004) Crack Pre-Activated Latest
imang66g
 
PDF
On Software Engineers' Productivity - Beyond Misleading Metrics
Romén Rodríguez-Gil
 
PDF
Adobe Illustrator Crack Full Download (Latest Version 2025) Pre-Activated
imang66g
 
PPTX
Visualising Data with Scatterplots in IBM SPSS Statistics.pptx
Version 1 Analytics
 
PPTX
Role Of Python In Programing Language.pptx
jaykoshti048
 
PPTX
Explanation about Structures in C language.pptx
Veeral Rathod
 
PPTX
Presentation about Database and Database Administrator
abhishekchauhan86963
 
Key Features to Look for in Arizona App Development Services
Net-Craft.com
 
Enhancing Healthcare RPM Platforms with Contextual AI Integration
Cadabra Studio
 
TRAVEL APIs | WHITE LABEL TRAVEL API | TOP TRAVEL APIs
philipnathen82
 
WatchTraderHub - Watch Dealer software with inventory management and multi-ch...
WatchDealer Pavel
 
Presentation about variables and constant.pptx
safalsingh810
 
Maximizing Revenue with Marketo Measure: A Deep Dive into Multi-Touch Attribu...
bbedford2
 
Activate_Methodology_Summary presentatio
annapureddyn
 
Balancing Resource Capacity and Workloads with OnePlan – Avoid Overloading Te...
OnePlan Solutions
 
New Download FL Studio Crack Full Version [Latest 2025]
imang66g
 
49784907924775488180_LRN2959_Data_Pump_23ai.pdf
Abilash868456
 
slidesgo-unlocking-the-code-the-dynamic-dance-of-variables-and-constants-2024...
kr2589474
 
Using licensed Data Loss Prevention (DLP) as a strategic proactive data secur...
Q-Advise
 
New Download MiniTool Partition Wizard Crack Latest Version 2025
imang66g
 
vAdobe Premiere Pro 2025 (v25.2.3.004) Crack Pre-Activated Latest
imang66g
 
On Software Engineers' Productivity - Beyond Misleading Metrics
Romén Rodríguez-Gil
 
Adobe Illustrator Crack Full Download (Latest Version 2025) Pre-Activated
imang66g
 
Visualising Data with Scatterplots in IBM SPSS Statistics.pptx
Version 1 Analytics
 
Role Of Python In Programing Language.pptx
jaykoshti048
 
Explanation about Structures in C language.pptx
Veeral Rathod
 
Presentation about Database and Database Administrator
abhishekchauhan86963
 

Ecto DSL Introduction - Yurii Bodarev

  • 1. Ecto DSL Domain specific language for writing queries and interacting with databases in Elixir.
  • 2. Yurii Bodarev Back-end software developer twitter.com/bodarev_yurii github.com/yuriibodarev
  • 4. Brief contents • Elixir Data Structures • Basic data types and structures • Associative data structures • Structs • Pattern Matching • Ecto • Ecto.Repo • Ecto.Schema • Ecto.Changeset • Ecto.Query
  • 5. Basic data types and structures • Atoms • Lists • Tuples
  • 6. Atoms Atoms are constants where their name is their own value. iex> :hello :hello iex> :hello == :world false iex> true == :true true
  • 7. Lists (Linked) Lists are used to manage dynamic, variable-sized collections of data of any type. iex> [1, "abc", true, 3] [1, "abc", true, 3] iex> length([1, 2, 3]) 3
  • 8. List - recursive structure [head | tail] iex> [1 | [2, 3, 4]] [1, 2, 3, 4] [head | [head | [head | tail…]]] iex> [1 | [2 | [3 | [4 | []]]]] [1, 2, 3, 4]
  • 9. List - recursive structure iex> hd([1, 2, 3, 4]) 1 iex> tl([1, 2, 3, 4]) [2, 3, 4] iex> tl([2, 3, 4]) [3, 4] iex> tl([4]) []
  • 10. Tuples Tuples are untyped structures often used to group a fixed number of elements together. iex> tuple = {:ok, "hello"} {:ok, "hello"} iex> elem(tuple, 1) "hello" iex> tuple_size(tuple) 2
  • 11. Associative data structures • Keyword lists • Maps
  • 12. List as key-value data structure It is common to use a list of 2-item tuples as the representation of a key-value data structure iex> list = [{"a", 1}, {"b", 2}, {"c", 3}] [{"a", 1}, {"b", 2}, {"c", 3}] iex> List.keyfind(list, "b", 0) {"b", 2}
  • 13. Keyword lists When we have a list of tuples and the first item of the tuple (i.e. the key) is an atom, we call it a keyword list. iex> [{:a, 1}, {:b, 2}, {:c, 3}] [a: 1, b: 2, c: 3]
  • 14. Keyword lists Elixir supports a special syntax for defining such lists: [key: value] iex> [a: 1, b: 2, c: 3] == [{:a, 1}, {:b, 2}, {:c, 3}] true • Keys must be atoms. • Keys are ordered, as specified by the developer. • Keys can be given more than once.
  • 15. We can use all operations available to lists on keyword lists iex> list = [a: 1, c: 3, b: 2] [a: 1, c: 3, b: 2] iex> hd(list) {:a, 1} iex> tl(list) [c: 3, b: 2] iex> list[:a] 1 iex> newlist = [a: 0] ++ list [a: 0, a: 1, c: 3, b: 2] iex> newlist[:a] 0 iex> list[:d] nil
  • 16. Keyword lists - default mechanism for passing options to functions in Elixir iex> if true, do: "THIS" "THIS" iex> if false, do: "THIS", else: "THAT" "THAT" iex> if(false, [do: "THIS", else: "THAT"]) "THAT" When the keyword list is the last argument of a function, the square brackets are optional.
  • 17. Example of the Ecto query query = from w in Weather, where: w.prcp > 0, where: w.temp < 20, select: w
  • 18. Maps A map is a key-value store, where keys and values can be any term. A map is created using the %{} syntax. Maps’ keys do not follow developer ordering. iex> map = %{:a => 1, 2 => "b", "c" => 3} %{2 => "b", :a => 1, "c" => 3} iex> map[:a] 1 iex> map[2] "b" iex> map["d"] nil
  • 19. Maps When all the keys in a map are atoms, you can use the keyword syntax. iex> map = %{a: 1, b: 2, c: 3} %{a: 1, b: 2, c: 3} iex> map.a 1 iex> map.d ** (KeyError) key :d not found in: %{a: 1, b: 2, c: 3} iex> %{map | c: 5} %{a: 1, b: 2, c: 5} iex> %{map | d: 0} ** (KeyError) key :d not found in: %{a: 1, b: 2, c: 3}
  • 20. Structs Structs are extensions built on top of maps that provide compile-time checks and default values. iex> defmodule User do ...> defstruct name: "Ivan", age: 25 ...> end iex> %User{} %User{age: 25, name: "Ivan"}
  • 21. Structs iex> %User{name: "Maria"} %User{age: 25, name: "Maria"} iex> %User{other: "Something"} ** (KeyError) key :other not found in: %User{age: 25, name: "Ivan"}
  • 22. Structs are bare maps underneath, but none of the protocols implemented for maps are available for structs iex> ivan = %User{} %User{age: 25, name: "Ivan"} iex> is_map(ivan) true iex> Map.keys(ivan) [:__struct__, :age, :name] iex> ivan.__struct__ User iex> ivan[:age] ** (UndefinedFunctionError) function User.fetch/2 is undefined (User does not implement the Access behaviour)
  • 23. Pattern matching iex> {a, b, c} = {:hello, "world", 42} {:hello, "world", 42} iex> a :hello iex> b "world" iex> c 42
  • 24. A pattern match will error if the sides can’t be matched iex> {a, b, c} = {:hello, "world"} ** (MatchError) no match of right hand side value: {:hello, "world"} iex> {a, b, c} = [:hello, "world", 42] ** (MatchError) no match of right hand side value: [:hello, "world", 42]
  • 25. We can match on specific values iex> {:ok, result} = {:ok, 13} {:ok, 13} iex> result 13 iex> {:ok, result} = {:error, "Not Found!"} ** (MatchError) no match of right hand side value: {:error, "Not Found!"}
  • 26. We can match on specific values post = Repo.get!(Post, 42) case Repo.delete post do {:ok, struct} -> # Deleted with success {:error, changeset} -> # Something went wrong end
  • 27. Pattern match on lists iex> [a, b, c] = [1, 2, 3] [1, 2, 3] iex> b 2 iex> [head | tail] = [1, 2, 3] [1, 2, 3] iex> head 1 iex> tail [2, 3] iex> [] = [1, 2, 3] ** (MatchError) no match of right hand side value: [1, 2, 3]
  • 28. Pattern match on keyword lists iex> [a: a] = [a: 1] [a: 1] iex> [a: a] = [a: 1, b: 2] ** (MatchError) no match of right hand side value: [a: 1, b: 2] iex> [b: b, a: a] = [a: 1, b: 2] ** (MatchError) no match of right hand side value: [a: 1, b: 2]
  • 29. Pattern match on maps iex> %{} = %{a: 1, b: 2} %{a: 1, b: 2} iex> %{b: b} = %{a: 1, b: 2} %{a: 1, b: 2} iex> b 2 iex> %{c: c} = %{a: 1, b: 2} ** (MatchError) no match of right hand side value: %{a: 1, b: 2}
  • 30. The pin ^ operator and _ iex> x = 2 2 iex> {1, ^x} = {1, 2} {1, 2} iex> {a, _} = {1, 2} {1, 2} iex> a 1
  • 31. Ecto Ecto is split into 4 main components: • Ecto.Repo - repositories are wrappers around the data store. • Ecto.Schema - schemas are used to map any data source into an Elixir struct. • Ecto.Changeset - allow developers to filter, cast, and validate changes before we apply them to the data. • Ecto.Query - written in Elixir syntax, queries are used to retrieve information from a given repository.
  • 32. Ecto playground Ecto in not an ORM github.com/yuriibodarev/Ecto_not_ORM Requires: PostgreSQL Run within IEx console: iex -S mix
  • 33. Repositories Via the repository, we can create, update, destroy and query existing database entries.
  • 34. Repositories Ecto.Repo is a wrapper around the database. We can define a repository as follows (libblogrepo.ex): defmodule Blog.Repo do use Ecto.Repo, otp_app: :blog end
  • 35. Repositories A repository needs an adapter and credentials to communicate to the database. Configuration for the Repo usually defined in your config/config.exs: config :blog, Blog.Repo, adapter: Ecto.Adapters.Postgres, database: "blog_repo", username: "postgres", password: "postgres", hostname: "localhost"
  • 36. Repositories Each repository in Ecto defines a start_link/0. Usually this function is invoked as part of your application supervision tree (libblog.ex): def start(_type, _args) do import Supervisor.Spec, warn: false children = [ worker(Blog.Repo, []), ] opts = [strategy: :one_for_one, name: Blog.Supervisor] Supervisor.start_link(children, opts) end
  • 37. Schema Schemas allows developers to define the shape of their data. (libbloguser.ex) defmodule Blog.User do use Ecto.Schema schema "users" do field :name, :string field :reputation, :integer, default: 0 has_many :posts, Blog.Post, on_delete: :delete_all timestamps end end
  • 38. Schema By defining a schema, Ecto automatically defines a struct: iex> user = %Blog.User{name: "Bill"} %Blog.User{__meta__: #Ecto.Schema.Metadata<:built, "users">, id: nil, inserted_at: nil, name: "Bill"}, posts: #Ecto.Association.NotLoaded<association :posts is not loaded>, reputation: 0, updated_at: nil}
  • 39. Schema Using Schema we can interact with a repository: iex> user = %Blog.User{name: "Bill", reputation: 10} %Blog.User{…} iex> Blog.Repo.insert!(user) %Blog.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">, id: 6, inserted_at: ~N[2016-12-13 16:16:35.983000], name: "Bill", posts: #Ecto.Association.NotLoaded<association :posts is not loaded>, reputation: 10, updated_at: ~N[2016-12-13 16:16:36.001000]}
  • 40. Schema # Get the user back iex> newuser = Blog.Repo.get(Blog.User, 6) iex> newuser.id 6 # Delete it iex> Blog.Repo.delete(newuser) {:ok, %Blog.User{…, id: 6,…}}
  • 41. Schema We can use pattern matching on Structs created with Schemas: iex> %{name: name, reputation: reputation} = ...> Blog.Repo.get(Blog.User, 1) iex> name "Alex" iex> reputation 144
  • 42. Changesets We can add changesets to our schemas to validate changes before we apply them to the data (libbloguser.ex): def changeset(user, params %{}) do user |> cast(params, [:name, :reputation]) |> validate_required([:name, :reputation]) |> validate_inclusion(:reputation, -999..999) end
  • 43. Changesets iex> alina = %Blog.User{name: "Alina"} iex> correct_changeset = Blog.User.changeset(alina, %{reputation: 55}) #Ecto.Changeset<action: nil, changes: %{reputation: 55}, errors: [], data: #Blog.User<>, valid?: true> iex> invalid_changeset = Blog.User.changeset(alina, %{reputation: 1055}) #Ecto.Changeset<action: nil, changes: %{reputation: 1055}, errors: [reputation: {"is invalid", [validation: :inclusion]}], data: #Blog.User<>, valid?: false>
  • 44. Changeset with Repository functions iex> valid_changeset.valid? true iex> Blog.Repo.insert(valid_changeset) {:ok, %Blog.User{…, id: 7, …}}
  • 45. Changeset with Repository functions iex> invalid_changeset.valid? false iex> Blog.Repo.insert(invalid_changeset) {:error, #Ecto.Changeset<action: :insert, changes: %{reputation: 1055}, errors: [reputation: {"is invalid", [validation: :inclusion]}], data: #Blog.User<>, valid?: false>}
  • 46. Changeset with Repository functions case Blog.Repo.update(changeset) do {:ok, user} -> # user updated {:error, changeset} -> # an error occurred end
  • 47. We can provide different changeset functions for different use cases def registration_changeset(user, params) do # Changeset on create end def update_changeset(user, params) do # Changeset on update end
  • 48. Query Ecto allows you to write queries in Elixir and send them to the repository, which translates them to the underlying database.
  • 49. Query using predefined Schema # Query using predefined Schema query = from u in User, where: u.reputation > 35, select: u # Returns %User{} structs matching the query Repo.all(query) [%Blog.User{…, id: 2, …, name: "Bender", …, reputation: 42, …}, %Blog.User{…, id: 1, …, name: "Alex", …, reputation: 144, …}]
  • 50. Directly querying the “users” table # Directly querying the “users” table query = from u in "users", where: u.reputation > 30, select: %{name: u.name, reputation: u.reputation} # Returns maps as defined in select Repo.all(query) [%{name: "Bender", reputation: 42}, %{name: "Alex", reputation: 144}]
  • 51. External values in Queries # ^ operator min = 33 query = from u in "users", where: u.reputation > ^min, select: u.name # casting mins = "33" query = from u in "users", where: u.reputation > type(^mins, :integer), select: u.name
  • 52. External values in Queries If query is created with predefined Schema than Ecto will automatically cast external value min = "35" Repo.all(from u in User, where: u.reputation > ^min) You can also skip Select to retrieve all fields specified in the Schema
  • 53. Associations schema "users" do field :name, :string field :reputation, :integer, default: 0 has_many :posts, Blog.Post, on_delete: :delete_all timestamps end
  • 54. Associations alex = Repo.get_by(User, name: "Alex") %Blog.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">, id: 1, inserted_at: ~N[2016-12-17 06:36:54.916000], name: "Alex", posts: #Ecto.Association.NotLoaded<association :posts is not loaded>, reputation: 13, updated_at: ~N[2016-12-17 06:36:54.923000]}
  • 55. Associations alex_wposts = Repo.preload(alex, :posts) %Blog.User{…, id: 1, name: "Alex", posts: [%Blog.Post{__meta__: #Ecto.Schema.Metadata<:loaded, "posts">, body: "World", comments: #Ecto.Association.NotLoaded<association :comments is not loaded>, id: 1, inserted_at: ~N[2016-12-17 06:36:54.968000], pinned: true, title: "Hello", updated_at: ~N[2016-12-17 06:36:54.968000], user: #Ecto.Association.NotLoaded<association :user is not loaded>, user_id: 1}], …}
  • 56. Associations users_wposts = from u in User, where: u.reputation > 10, order_by: [asc: u.name], preload: [:posts]
  • 57. Queries Composition alex = Repo.get_by(User, name: "Alex") alex_post = from p in Post, where: p.user_id == ^alex.id alex_pin = from ap in alex_post, where: ap.pinned == true
  • 58. Fragments title = "hello" from(p in Post, where: fragment("lower(?)", p.title) == ^title) |> Repo.all()