Brincando com Ecto.Query
Tags: elixir, ecto, database, sql
Published: 2022-06-25 22:58:38

# Introdução

Sejam bem-vindos, fico imensamente feliz com seu interesse em aprender um pouco mais sobre o Ecto e suas funcionalidades do modulo `Ecto.Query` para construir queries SQL

Vamos usar o projeto do post [Introdução ao Ecto](https://dev.to/rohlacanna/introducao-ao-ecto-mh0) para brincarmos um pouco com as queries sql.

###### Recomendo ler o projeto anterior sobre `Ecto` para melhor compreensão.

## Schemas utilizado para este projeto:

#### Módulo User

```elixir
defmodule Ecto4noobs.User do
  use Ecto.Schema

  import Ecto.Changeset

  alias Ecto4noobs.Movie

  schema "users" do
    field(:name, :string)
    field(:email, :string)
    field(:age, :integer)

    belongs_to(:movie, Movie)
  end
end
```

#### Módulo Movie

```elixir
defmodule Ecto4noobs.Movie do
  use Ecto.Schema

  import Ecto.Changeset

  alias Ecto4noobs.User

  schema "movies" do
    field(:title, :string)
    field(:director, :string)
    field(:year, :integer)

    has_many(:users, User)
  end
end
```

## Relação Belongs To/Has Many

Vamos iniciar com dois schemas: User e Movie. Vamos implementar uma relação `belongs_to/has_many` entre os dois: Um filme tem vários (has many) usuários e um usuário pertence a (belongs to) um filme.

### Schema Belongs To

Belongs To utiliza a chave estrangeira para tornar o filme associado a um usuário disponível quando executamos a consulta.

### Schema Has Many

Has Many não adiciona dados ao banco de dados por si só. O que ela faz é utilizar uma `chave estrangeira` no schema associado (users).

## Listando os filmes

```bash
from(m in Movie) |> Repo.all()
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vd3y4785gfnkfevdauix.png)

Se notarmos, veremos que temos a seguinte mensagem: `NotLoadAssociation`. Caso a gente queria listar os `usuários` juntamente aos `filmes`, apenas essa `query` não é o suficiente.

Para carregas as associações, faremos:

```bash
query = from(m in Movie, preload: [:users]) |> Repo.all()
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/evy7j0o05m0e145ttxt6.png) 

## Exemplos de query com filtros where:

Buscando os usuários com idade menos que 20.

```bash
query = from(u in User, where: u.age < 20)
```

`U` representa uma linha da tabela `User` e em seguida a gente passa a condição que queremos filtrar, que no caso queremos filtrar a idade que for menor que 20.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p4shkca9oda8wa5wcpwh.png)

## Antes de continuarmos vamos aprender um pouco sobre `SQL JOIN`

### O que é um JOIN?

Bom, o join combina duas tabelas através de alguma chave ou valor comum entre elas.

Existem alguns tipos diferentes de JOIN:

- **INNER JOIN**: É um comando que permite a seleção de informações em diferentes tabelas, desde que a informação em questão seja compartilhada por ambas.

- **LEFT JOIN**: É um comando que retorna todos os registros da tabela à esquerda e os registros correspondentes da tabela à direita.

- **RIGHT JOIN**: Ao contrário do LEFT JOIN, o comando RIGHT JOIN retorna todos os dados encontrados na tabela à direita. Porém, se não tiver dados associados entre as tabelas esquerda e direita, ele retorna valores nulos.

### Usando INNER JOIN

```bash
query = 
from(u in User, 
inner_join: m in Movie, 
on: u.movie_id == m.id, 
select: [m.title, u.name])
```

Neste exemplo, estamos buscando os filmes e o nome do usuário que cada um recebeu. Para isso, começamos a nossa query na tabela `User`, depois fazemos um `inner_join` na tabela de `filmes` e cujo a condição do ID do filme do usuário é igual ao ID do filme. Por fim, a gente faz a seleção dos campos que queremos mostrar.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4696yjxjgx8ejck3w3z6.png)

### Usando LEFT JOIN

```bash
query = 
from(m in Movie, 
left_join: u in User, 
on: u.movie_id == m.id, 
where: u.name == "Floki O Gato" and m.title == "Um Sonho de Liberdade", 
select: [m.title, u.name])
```

Neste exemplo, buscamos filmes que contém o usuário "Floki O Gato". Para isso, começamos a query na tabela Movie, depois fazemos um `left_join` na tabela `User` e cujo a condição do ID do filme do usuário é igual ao ID do filme eu busco por usuário "Floki O Gato" onde o titulo do filme é "Um Sonho de Liberdade". Por fim, a gente faz a seleção dos campos que queremos mostrar.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bz6jmtm26fla3k1ozrl8.png)

### Usando LEFT EXCLUDING JOIN

Filmes que não tiveram nenhum usuário

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zzuw220sefqpt3r305ej.png)

### Usando RIGHT JOIN

```bash
query = 
from(m in Movie, 
right_join: u in User, 
on: u.movie_id == m.id, 
select: [m.title, u.name])
```

Neste exemplo, estou buscando os filmes com o usuário que foi recebido.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bgwuniclg0sb1tgdkufb.png)

### Usando RIGHT EXCLUDING JOIN

Buscar os filmes com os usuário que cada um recebeu

```bash
query = 
from(m in Movie, 
right_join: u in User, 
on: u.movie_id == m.id, 
where: not is_nil(u.name), 
select: [m.title, u.name])
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fufailpvot7es11uud8c.png)

### Fragment

Forma de chamar funções em SQL puro no Ecto

```bash
query = 
from(m in Movie, 
select: fragment("upper(?)", m.title))
```
Estamos fazendo uma listagem de títulos de filme em uppercase `fragment(upper)` que é uma função do Postgres.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/urn2ynh952abzvuqflgn.png)

*OU*

```bash
title = "O Poderoso Chefão"
query = from(m in Movie, 
where: m.title == ^title, 
select: fragment("upper(?)", m.title))
```

Quando queremos colocar um valor de uma variável dentro da querie a gente precisa colocar operador `pinar (^)` se não, dará erro de compilação.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ly88gz3exd4mu1bofeas.png)

### LIKE

Forma de busca por campo de texto

```bash
title = "O"
query = 
from(m in Movie, 
where: like(m.title, ^"#{title}%"), 
select: fragment("upper(?)", m.title))
```

Filtrando títulos de filme que começam com a letra `O`. 

Então, nesse nosso exemplo quero buscar os filmes que começam com O, então eu uso a função do `Ecto LIKE` passando o campo de texto que eu quero interpolar e o sinal de pinar, por fim o porcent `%` que é para dar `match` em qualquer termo.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1v9y69y9fr8l7aya34uo.png)

E é isso! =)

# Conclusão
Muito obrigado pela leitura até aqui e espero ter ajudado de alguma forma. Tem alguma sugestão ou encontrou algum problema? por favor deixe-me saber. 💜