SlideShare a Scribd company logo
1 of 59
Download to read offline
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

What's hot

Magicke metody v Pythonu
Magicke metody v PythonuMagicke metody v Pythonu
Magicke metody v Pythonu
Jirka Vejrazka
 
Python tutorialfeb152012
Python tutorialfeb152012Python tutorialfeb152012
Python tutorialfeb152012
Shani729
 
Python tutorial
Python tutorialPython tutorial
Python tutorial
Rajiv Risi
 

What's hot (19)

1. python
1. python1. python
1. python
 
Where's My SQL? Designing Databases with ActiveRecord Migrations
Where's My SQL? Designing Databases with ActiveRecord MigrationsWhere's My SQL? Designing Databases with ActiveRecord Migrations
Where's My SQL? Designing Databases with ActiveRecord Migrations
 
Python Modules and Libraries
Python Modules and LibrariesPython Modules and Libraries
Python Modules and Libraries
 
Magicke metody v Pythonu
Magicke metody v PythonuMagicke metody v Pythonu
Magicke metody v Pythonu
 
Introduction to python programming
Introduction to python programmingIntroduction to python programming
Introduction to python programming
 
Python programming : Arrays
Python programming : ArraysPython programming : Arrays
Python programming : Arrays
 
Functional es6
Functional es6Functional es6
Functional es6
 
Ggplot2 v3
Ggplot2 v3Ggplot2 v3
Ggplot2 v3
 
Swift에서 꼬리재귀 사용기 (Tail Recursion)
Swift에서 꼬리재귀 사용기 (Tail Recursion)Swift에서 꼬리재귀 사용기 (Tail Recursion)
Swift에서 꼬리재귀 사용기 (Tail Recursion)
 
Python tutorialfeb152012
Python tutorialfeb152012Python tutorialfeb152012
Python tutorialfeb152012
 
Python Workshop Part 2. LUG Maniapl
Python Workshop Part 2. LUG ManiaplPython Workshop Part 2. LUG Maniapl
Python Workshop Part 2. LUG Maniapl
 
Farhana shaikh webinar_dictionaries
Farhana shaikh webinar_dictionariesFarhana shaikh webinar_dictionaries
Farhana shaikh webinar_dictionaries
 
Python dictionary : past, present, future
Python dictionary: past, present, futurePython dictionary: past, present, future
Python dictionary : past, present, future
 
Functional Programming
Functional ProgrammingFunctional Programming
Functional Programming
 
Python tutorial
Python tutorialPython tutorial
Python tutorial
 
Begin with Python
Begin with PythonBegin with Python
Begin with Python
 
Closure, Higher-order function in Swift
Closure, Higher-order function in SwiftClosure, Higher-order function in Swift
Closure, Higher-order function in Swift
 
Monads and Monoids by Oleksiy Dyagilev
Monads and Monoids by Oleksiy DyagilevMonads and Monoids by Oleksiy Dyagilev
Monads and Monoids by Oleksiy Dyagilev
 
Python programming Part -6
Python programming Part -6Python programming Part -6
Python programming Part -6
 

Similar to Ecto DSL Introduction - Yurii Bodarev

Python Interview Questions | Python Interview Questions And Answers | Python ...
Python Interview Questions | Python Interview Questions And Answers | Python ...Python Interview Questions | Python Interview Questions And Answers | Python ...
Python Interview Questions | Python Interview Questions And Answers | Python ...
Simplilearn
 

Similar to Ecto DSL Introduction - Yurii Bodarev (20)

Introducción a Elixir
Introducción a ElixirIntroducción a Elixir
Introducción a Elixir
 
Python Programming: Lists, Modules, Exceptions
Python Programming: Lists, Modules, ExceptionsPython Programming: Lists, Modules, Exceptions
Python Programming: Lists, Modules, Exceptions
 
Introduction to Elixir
Introduction to ElixirIntroduction to Elixir
Introduction to Elixir
 
Arrays and function basic c programming notes
Arrays and function basic c programming notesArrays and function basic c programming notes
Arrays and function basic c programming notes
 
MATLAB Programming
MATLAB Programming MATLAB Programming
MATLAB Programming
 
An overview of Python 2.7
An overview of Python 2.7An overview of Python 2.7
An overview of Python 2.7
 
A tour of Python
A tour of PythonA tour of Python
A tour of Python
 
Matlab solved problems
Matlab solved problemsMatlab solved problems
Matlab solved problems
 
Matlab-1.pptx
Matlab-1.pptxMatlab-1.pptx
Matlab-1.pptx
 
Basic R Data Manipulation
Basic R Data ManipulationBasic R Data Manipulation
Basic R Data Manipulation
 
Introduction to Elixir
Introduction to ElixirIntroduction to Elixir
Introduction to Elixir
 
Using-Python-Libraries.9485146.powerpoint.pptx
Using-Python-Libraries.9485146.powerpoint.pptxUsing-Python-Libraries.9485146.powerpoint.pptx
Using-Python-Libraries.9485146.powerpoint.pptx
 
Python Interview Questions | Python Interview Questions And Answers | Python ...
Python Interview Questions | Python Interview Questions And Answers | Python ...Python Interview Questions | Python Interview Questions And Answers | Python ...
Python Interview Questions | Python Interview Questions And Answers | Python ...
 
ClojurianからみたElixir
ClojurianからみたElixirClojurianからみたElixir
ClojurianからみたElixir
 
R workshop
R workshopR workshop
R workshop
 
C++ process new
C++ process newC++ process new
C++ process new
 
scripting in Python
scripting in Pythonscripting in Python
scripting in Python
 
A Few of My Favorite (Python) Things
A Few of My Favorite (Python) ThingsA Few of My Favorite (Python) Things
A Few of My Favorite (Python) Things
 
R tutorial for a windows environment
R tutorial for a windows environmentR tutorial for a windows environment
R tutorial for a windows environment
 
Basic operations by novi reandy sasmita
Basic operations by novi reandy sasmitaBasic operations by novi reandy sasmita
Basic operations by novi reandy sasmita
 

More from Elixir Club

More from Elixir Club (20)

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

Recently uploaded

introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdfintroduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
VishalKumarJha10
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
shinachiaurasa2
 

Recently uploaded (20)

Sector 18, Noida Call girls :8448380779 Model Escorts | 100% verified
Sector 18, Noida Call girls :8448380779 Model Escorts | 100% verifiedSector 18, Noida Call girls :8448380779 Model Escorts | 100% verified
Sector 18, Noida Call girls :8448380779 Model Escorts | 100% verified
 
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
 
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation Template
 
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdfintroduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 
10 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 202410 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 2024
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
 

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()