<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Glaucia Esppenchutz - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Aprenda a codificar - de graça. Tutoriais de programação em Python, JavaScript, Linux e muito mais. ]]>
        </description>
        <link>https://www.freecodecamp.org/portuguese/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Glaucia Esppenchutz - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/portuguese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sat, 30 May 2026 08:36:25 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/portuguese/news/author/glaucia/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Qual é o Content-Type correto para um JSON? O cabeçalho de requisição de Mime explicado ]]>
                </title>
                <description>
                    <![CDATA[ Cada recurso usado na internet possui um tipo de mídia, também conhecido como tipo MIME (sigla em inglês para Multipurpose Internet Mail Extension, ou Extensão de Correio da Internet Multifuncional, em português). Essa informação é necessária para transações entre servidores e clients. O navegador precisa saber qual o tipo dos ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/qual-e-o-content-type-correto-para-um-json-o-cabecalho-de-requisicao-de-mime-explicado/</link>
                <guid isPermaLink="false">6346d791287f9a05e0d4d3eb</guid>
                
                    <category>
                        <![CDATA[ JSON ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Glaucia Esppenchutz ]]>
                </dc:creator>
                <pubDate>Tue, 17 Jan 2023 02:16:51 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2023/01/5fcf4739e6787e098393bd6d.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/what-is-the-correct-content-type-for-json-request-header-mime-type-explained/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">What is the Correct Content-Type for JSON? Request Header Mime Type Explained</a>
      </p><p>Cada recurso usado na internet possui um tipo de mídia, também conhecido como tipo MIME (sigla em inglês para <em>Multipurpose Internet Mail Extension</em>, ou Extensão de Correio da Internet Multifuncional, em português). Essa informação é necessária para transações entre servidores e <em>clients</em>.</p><p>O navegador precisa saber qual o tipo dos recursos de mídia que são enviados a ele e como lidar com esses recursos de maneira correta. </p><p>A mesma situação acontece com o servidor. Ele precisa saber o tipo de recursos que são enviados a ele para análise e processamento precisos.</p><p><strong>O que é a declaração de Content-Type?</strong></p><p>O tipo de mídia de qualquer recurso é declarado na propriedade <code>Content-Type</code> no cabeçalho de uma requisição (no <em>client</em>, quando fazendo a requisição ao servidor) e no cabeçalho de resposta (no servidor, quando enviando uma reposta).</p><p>Sem declarar explicitamente o tipo de conteúdo de uma fonte, o client pode tentar automaticamente detectar o tipo, mas o resultado pode não ser o mais preciso. Por isso a importância de o declarar explicitamente.</p><h2 id="tipos-de-m-dia">Tipos de mídia</h2><p>Os tipos de mídia existentes têm diversas formas. Eles são categorizados em vários grupos:</p><ul><li>aplicação (<code>application</code>)</li><li>áudio (<code>audio</code>)</li><li>fonte (<code>font</code>)</li><li>exemplo (<code>example</code>)</li><li>imagem (<code>image</code>)</li><li>mensagem (<code>message</code>)</li><li>modelo (<code>model</code>)</li><li>multiparte (<code>multipart</code>)</li><li>texto (<code>text</code>)</li><li>e vídeo (<code>video</code>)</li></ul><p>Essas categorias também têm seus tipos. Por exemplo, <code>application/json</code> é um tipo em <code>application</code> e <code>text/html</code> é um tipo em <code>text</code>.</p><p>Você pode encontrar uma lista completa dos tipos de mídia na <a href="https://www.iana.org/assignments/media-types/media-types.xhtml">IANA</a> (uma organização que coordena alguns dos elementos principais da internet).</p><p>Todos esses vários tipos de dados, como texto, áudio, imagens, HTML e tantos outros são usados por toda a internet.</p><h2 id="o-navegador-precisa-saber-o-tipo-de-m-dia-de-um-recurso"><strong>O navegador precisa saber o tipo de mídia de um recurso</strong></h2><p>Como mencionei acima, o navegador precisa saber que tipo de conteúdo ele recebe. Aqui temos um exemplo ilustrativo.</p><p>O código abaixo é um servidor do Node que serve um arquivo HTML:</p><pre><code class="language-js">const http = require("http");
const fs = require("fs");
const path = require("path");

const server = http.createServer(function (req, res) {
	const filePath = path.join(__dirname, "index.html");
	var stat = fs.statSync(filePath);

	res.writeHead(200, {
		"Content-Type": "text/css",
		"Content-Length": stat.size,
	});

	const readStream = fs.createReadStream(filePath);
	readStream.pipe(res);
});

server.listen(5000);

console.log("Servidor da web do Node.js na porta 5000 em execução.");
</code></pre><p>Não se preocupe com as especificidades do código. O que importa aqui é o arquivo <code>index.html</code> que estamos servindo e que <code>Content-Type</code> é <code>text/css</code>.</p><p>Aqui está o conteúdo de <code>index.html</code>:</p><pre><code class="language-html">&lt;h1&gt;Homepage&lt;/h1&gt;
</code></pre><p>Logicamente, um documento HTML é diferente de um arquivo CSS. Aqui está o resultado em <code>localhost:5000</code> quando o servidor é iniciado:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/01/Screenshot-2020-12-08-at-10.12.32.png" class="kg-image" alt="Screenshot-2020-12-08-at-10.12.32" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/01/Screenshot-2020-12-08-at-10.12.32.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2023/01/Screenshot-2020-12-08-at-10.12.32.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2023/01/Screenshot-2020-12-08-at-10.12.32.png 1262w" sizes="(min-width: 720px) 720px" width="600" height="400" loading="lazy"></figure><p>Você também pode confirmar a resposta obtida conferindo os cabeçalhos na aba Rede (ou em inglês, <em>Network</em>) das ferramentas do desenvolvedor.</p><p>Aqui vemos o resultado em um navegador do Google Chrome:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/01/Screenshot-2020-12-08-at-10.13.34.png" class="kg-image" alt="Screenshot-2020-12-08-at-10.13.34" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/01/Screenshot-2020-12-08-at-10.13.34.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2023/01/Screenshot-2020-12-08-at-10.13.34.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2023/01/Screenshot-2020-12-08-at-10.13.34.png 1232w" sizes="(min-width: 720px) 720px" width="600" height="400" loading="lazy"></figure><p>O navegador obteve o conteúdo como um tipo CSS. Por isso, tentou tratá-lo como um arquivo CSS.</p><p>Observe também que o conhecimento pleno do tipo de conteúdo obtido pelo navegador reduz vulnerabilidades de segurança, pois o navegador conhece os padrões de segurança a serem utilizados para os dados em questão.</p><p>Agora que você compreendeu o conceito de tipos MIME e sua importância, vamos tratar do JSON.</p><h2 id="o-tipo-correto-de-content-type-para-json"><strong>O tipo correto de<strong> Content-Type </strong>para <strong>JSON</strong></strong></h2><p>O JSON precisa ser interpretado corretamente pelo navegador para ser usado adequadamente. <code>text/plain</code> era tipicamente usado para JSON, mas, de acordo com a <a href="https://www.iana.org/assignments/media-types/media-types.xhtml">IANA</a>, o tipo MIME oficial para o JSON é <code>application/json</code>.</p><p>Isso significa que, quando você estiver enviando JSON para o servidor ou recebendo JSON do servidor, deve sempre declarar o <code>Content-Type</code> do cabeçalho como <code>application/json</code>, pois esse é o padrão que tanto o <em>client </em>quanto o servidor entendem.</p><h2 id="conclus-o"><strong><strong>Conclus</strong>ão</strong></h2><p>Como vimos acima, o servidor (assim como o navegador) precisa saber o tipo de dados enviados a ele, por exemplo, em uma solicitação de POST. Esse é o motivo pelo qual <code>forms</code> com arquivos geralmente contêm o atributo <code>enctype</code> com o valor de <code>multipart/form-data</code>.</p><p>Sem codificar a solicitação dessa maneira, a solicitação de POST não dará certo. Além disso, quando o servidor sabe o tipo de dados que foi enviado para ele, ele saberá como fazer o <em>parsing </em>dos dados codificados.</p><p>Neste artigo, examinamos o que são os tipos MIME e sua finalidade. Vimos também qual é o tipo oficial de conteúdo para JSON. Espero que você agora saiba da importância de declarar os tipos de recursos quando utilizá-los na internet.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Como testar serviços, endpoints e repositórios com o SpringBoot ]]>
                </title>
                <description>
                    <![CDATA[ Escrito por: Emre Savcı Neste artigo, mostrarei como escrever testes unitários em aplicações do SpringBoot. A razão da necessidade de escrever testes unitários pede um outro artigo para explicar. Para uma breve explicação, no entanto, vou contar a você algumas coisas. Normalmente, eu defendo o argumento de que um código ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/como-testar-servicos-endpoints-e-repositorios-com-o-springboot/</link>
                <guid isPermaLink="false">63320ea55c046e06ec87b0ce</guid>
                
                    <category>
                        <![CDATA[ Spring Boot ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Glaucia Esppenchutz ]]>
                </dc:creator>
                <pubDate>Thu, 17 Nov 2022 21:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/1_aH_ifsVI0cI5P5Guu1X1CQ.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/unit-testing-services-endpoints-and-repositories-in-spring-boot-4b7d9dc2b772/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to test services, endpoints, and repositories in Spring Boot</a>
      </p><p>Escrito por: Emre Savcı</p><p>Neste artigo, mostrarei como escrever testes unitários em aplicações do SpringBoot.</p><p>A razão da necessidade de escrever testes unitários pede um outro artigo para explicar. Para uma breve explicação, no entanto, vou contar a você algumas coisas.</p><p>Normalmente, eu defendo o argumento de que um código sem testes unitários é um código morto. Isso porque, quando um desenvolvedor adiciona uma nova funcionalidade a um código que não é coberta por testes unitários, fica propenso a sobrescrever alguma regra de negócio (o que mata o código que foi escrito anteriormente). Talvez não fique exatamente propenso, mas você consegue imaginar que podem acontecer erros quando um projeto precisar ser modificado. Testes unitários são a única forma de proteger seu código contra quebras que podem ocorrer em mudanças.</p><h4 id="por-que-testes-unit-rios-em-enpoints"><strong>Por que testes unitários em <em>enpoints</em>?</strong></h4><p>Toda vez que escrevemos um endpoint, precisamos ter certeza de que algumas coisas estão funcionando corretamente. O endpoint deve retornar os dados em uma estrutura correta e lidar com a requisição corretamente. Podemos testar manualmente, o que não é preferível. Então, escrevemos testes unitários para garantir que nossos endpoints funcionam corretamente. Existe também uma forma de testar endpoints usando testes automatizados, mas não é o objetivo deste artigo.</p><h4 id="por-que-testes-unit-rio-em-servi-os"><strong>Por que<strong><strong><strong> </strong></strong></strong>testes unitário em serviços?</strong></h4><p>Isso já deveria ser claro, mas, em todo caso: precisamos ter certeza que nossa regra de negócio funciona corretamente.</p><h4 id="por-que-testes-unit-rios-em-reposit-rios"><strong>Por que testes unitários em repositórios?</strong></h4><p>Existem alguns casos para testar repositórios. É claro que não testamos o próprio framework. Porém, escrevemos testes unitários para ter a certeza de que nossas especificações ou relações foram implementadas corretamente.</p><h3 id="como-testamos-controllers"><strong>Como testamos <em>controllers</em>?</strong></h3><p>Agora é a hora de mostrar a você como testar nossos <em>controllers</em> no SpringBoot. Vamos imaginar que escrevemos uma aplicação que nos permite salvar usuários no nosso banco de dados. Definimos uma entidade <em>user</em> (usuário), um serviço para o usuário e um <em>controller</em>.</p><p><em>Observação: os exemplos demonstrados nesse artigo não são para arquiteturas de aplicações reais em produção.</em></p><pre><code>@Data@Entitypublic class User {    @Id    @GeneratedValue(generator = "uuid2")    @GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator")    @Column(name = "id", columnDefinition = "BINARY(16)")    private UUID id;    private String name;    private String email;    private int age;}</code></pre><pre><code>@Datapublic class CreateUserRequest {    private String name;    private String email;    private int age;}</code></pre><pre><code>@RestController@RequestMapping("/users")public class UserController {    UserService userService;    @Autowired    public UserController(UserService userService) {        this.userService = userService;    }    @PostMapping    public ResponseEntity&lt;User&gt; createUser(@RequestBody CreateUserRequest request) {        User created = userService.save(request);        return ResponseEntity.ok(created);    }}</code></pre><p>Nossos <em>controllers</em> tem uma dependência no UserService, mas não estamos interessados no que o serviço faz agora.</p><p>Agora vamos escrever um teste unitário para nosso <em>controller</em> e garantir que ele funciona corretamente.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/ZdQMKmxKk9mlD-AzNuUZpQxdCZ4VSi4ELW6d.png" class="kg-image" alt="ZdQMKmxKk9mlD-AzNuUZpQxdCZ4VSi4ELW6d" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/11/ZdQMKmxKk9mlD-AzNuUZpQxdCZ4VSi4ELW6d.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/11/ZdQMKmxKk9mlD-AzNuUZpQxdCZ4VSi4ELW6d.png 800w" sizes="(min-width: 720px) 720px" width="800" height="557" loading="lazy"></figure><p>Simulamos nosso serviço porque ainda não temos sua implementação em detalhes. Testamos nosso <em>controller</em> apenas aqui. Usamos o <code>MockMvc</code> para testar nosso <em>controller</em> e mapear nosso objeto para fins de serialização.</p><p>Configuramos o método <code>userService.Save()</code> para retornar nosso objeto <em>user</em> desejado. Passamos uma requisição para nosso <em>controller</em> e depois verificamos os dados retornados na seguinte linha:</p><p> <code>andExpect(jsonPath("$.name").value(request.getName()))</code> .</p><p>Também temos <a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/web/servlet/result/JsonPathResultMatchers.html">outros métodos que podem ser usados</a> (documentação do Spring Boot, em inglês). Aqui está a lista:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/XNRt3lBVu-47pwdDQOg73BhX-Fy3OhCr-mZf.png" class="kg-image" alt="XNRt3lBVu-47pwdDQOg73BhX-Fy3OhCr-mZf" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/11/XNRt3lBVu-47pwdDQOg73BhX-Fy3OhCr-mZf.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/11/XNRt3lBVu-47pwdDQOg73BhX-Fy3OhCr-mZf.png 800w" sizes="(min-width: 720px) 720px" width="800" height="479" loading="lazy"></figure><p>Quando executamos o teste, vemos que ele passa:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/Gx1sVQbUTi3rn7DiqgGQuMUVN60-4xWHIHji.png" class="kg-image" alt="Gx1sVQbUTi3rn7DiqgGQuMUVN60-4xWHIHji" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/11/Gx1sVQbUTi3rn7DiqgGQuMUVN60-4xWHIHji.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/11/Gx1sVQbUTi3rn7DiqgGQuMUVN60-4xWHIHji.png 800w" sizes="(min-width: 720px) 720px" width="800" height="228" loading="lazy"></figure><h3 id="como-testamos-nosso-servi-o"><strong>Como testamos nosso serviço?</strong></h3><p>Agora, vamos testar o <em>UserService</em>. Ele é bem simples de testar.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/5ALGlLPT49JTxgUal22WbDbfrEhjBHD24eSV.png" class="kg-image" alt="5ALGlLPT49JTxgUal22WbDbfrEhjBHD24eSV" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/11/5ALGlLPT49JTxgUal22WbDbfrEhjBHD24eSV.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/11/5ALGlLPT49JTxgUal22WbDbfrEhjBHD24eSV.png 800w" sizes="(min-width: 720px) 720px" width="800" height="444" loading="lazy"></figure><p>Simulamos um repositório e injetamos nele um <em>UserService</em>. Agora, quando executarmos o teste, veremos que ele passa.</p><p>Vamos adicionar uma regra de negócio ao <em>UserService</em>: digamos que o usuário precisa ter um endereço de e-mail.</p><p>Vamos mudar nosso método de salvamento no <em>UserService</em>:</p><pre><code>public User save(CreateUserRequest request) {    requireNonNull(request.getEmail());        User user = new User();    user.setName(request.getName());    user.setEmail(request.getEmail());    user.setAge(request.getAge());    userRepository.save(user);    return user;}</code></pre><p>Quando executamos o teste novamente, veremos que o teste falha.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/HhoeIyT6oUhvYVcCD2nbZepI3S-qbPfhUE5n.png" class="kg-image" alt="HhoeIyT6oUhvYVcCD2nbZepI3S-qbPfhUE5n" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/11/HhoeIyT6oUhvYVcCD2nbZepI3S-qbPfhUE5n.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/11/HhoeIyT6oUhvYVcCD2nbZepI3S-qbPfhUE5n.png 800w" sizes="(min-width: 720px) 720px" width="800" height="160" loading="lazy"></figure><p>Antes de consertarmos, vamos escrever um teste que satisfaça essa regra.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/VZqkwBqS14vzxKBcMWdtHPES1xo8XxK3ht3I.png" class="kg-image" alt="VZqkwBqS14vzxKBcMWdtHPES1xo8XxK3ht3I" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/11/VZqkwBqS14vzxKBcMWdtHPES1xo8XxK3ht3I.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/11/VZqkwBqS14vzxKBcMWdtHPES1xo8XxK3ht3I.png 800w" sizes="(min-width: 720px) 720px" width="800" height="175" loading="lazy"></figure><p>Escrevemos um novo teste que especificou que, se enviarmos um e-mail nulo, ele lançará uma <code>NullPointerException.</code></p><p>Vamos agora consertar o teste adicionando um e-mail à nossa requisição:</p><pre><code>createUserRequest.setEmail("testemail");</code></pre><p>Executando ambos os testes:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/hFTPEmxhpDT6WVsWc3VpeQ0Y9XOGwBdwr05u.png" class="kg-image" alt="hFTPEmxhpDT6WVsWc3VpeQ0Y9XOGwBdwr05u" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/11/hFTPEmxhpDT6WVsWc3VpeQ0Y9XOGwBdwr05u.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/11/hFTPEmxhpDT6WVsWc3VpeQ0Y9XOGwBdwr05u.png 800w" sizes="(min-width: 720px) 720px" width="800" height="102" loading="lazy"></figure><h3 id="como-testamos-nossos-reposit-rios"><strong>Como testamos nossos repositórios?</strong></h3><p>Agora, vamos testar nossos repositórios. Vamos usar um banco de dados h2 em memória com <code>TestEntityManager</code>.</p><p>Nosso repositório é definido assim:</p><pre><code>@Repositorypublic interface UserRepository extends JpaRepository&lt;User, Integer&gt;, JpaSpecificationExecutor&lt;User&gt; {    Optional&lt;User&gt; findById(UUID id);}</code></pre><p>Primeiro, configuramos o h2db. Criamos um arquivo chamado <em>application.yaml</em> em <em>test -&gt; resource</em>s:</p><pre><code>spring:  application:    name: Spring Boot Rest API  datasource:    type: com.zaxxer.hikari.HikariDataSource    url: "jdbc:h2:mem:test-api;INIT=CREATE SCHEMA IF NOT EXISTS dbo\\;CREATE SCHEMA IF NOT EXISTS definitions;DATABASE_TO_UPPER=false;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false;MODE=MSSQLServer"    name:    password:    username:    initialization-mode: never    hikari:      schema: dbo  jpa:    database: H2    database-platform: org.hibernate.dialect.H2Dialect    show-sql: true    hibernate:      ddl-auto: create-drop  test:    database:      replace: none</code></pre><p>Em seguida, escrevemos o primeiro teste básico para nosso repositório: salvar um usuário e recuperá-lo: </p><pre><code>@RunWith(SpringRunner.class)@DataJpaTestpublic class UserRepositoryTest {    @Autowired    TestEntityManager entityManager;    @Autowired    UserRepository sut;    @Test    public void it_should_save_user() {        User user = new User();        user.setName("test user");        user = entityManager.persistAndFlush(user);        assertThat(sut.findById(user.getId()).get()).isEqualTo(user);    }}</code></pre><p>Quando executamos veremos algumas saídas no console, e nosso teste também passa:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/ZvTQXeFLK6VNU5jrP7Xl61vbBwNi6wJMJnHR.png" class="kg-image" alt="ZvTQXeFLK6VNU5jrP7Xl61vbBwNi6wJMJnHR" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/11/ZvTQXeFLK6VNU5jrP7Xl61vbBwNi6wJMJnHR.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/11/ZvTQXeFLK6VNU5jrP7Xl61vbBwNi6wJMJnHR.png 800w" sizes="(min-width: 720px) 720px" width="800" height="203" loading="lazy"></figure><p>Agora, vamos adicionar outro método ao nosso repositório para buscar um usuário através do seu e-mail:</p><pre><code>Optional&lt;User&gt; findByEmail(String email);</code></pre><p>Escrevemos, por fim, outro teste:</p><pre><code>@Testpublic void it_should_find_user_byEmail() {    User user = new User();    user.setEmail("testmail@test.com");    user = entityManager.persistAndFlush(user);    assertThat(sut.findByEmail(user.getEmail()).get()).isEqualTo(user);}</code></pre><p>Quando damos uma olhada no console depois de executar o teste, vemos o SQL gerado pelo <em>hibernate</em>:</p><pre><code>SELECT user0_.id AS id1_1_,user0_.age AS age2_1_,user0_.email AS email3_1_,user0_.name AS name4_1_FROM user user0_WHERE user0_.email=?</code></pre><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/817OJk5EOmr4TjTvg9Pcul7Ki6fdpNMqbYqP.png" class="kg-image" alt="817OJk5EOmr4TjTvg9Pcul7Ki6fdpNMqbYqP" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/11/817OJk5EOmr4TjTvg9Pcul7Ki6fdpNMqbYqP.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/11/817OJk5EOmr4TjTvg9Pcul7Ki6fdpNMqbYqP.png 800w" sizes="(min-width: 720px) 720px" width="800" height="210" loading="lazy"></figure><p>Até agora, tudo bem. Cobrimos o básico de testes unitários com o SpringBoot.</p><p>Agora, não temos mais desculpas para não escrever testes unitários! Espero que tenha sido claro para você como escrever testes unitários para cada tipo de propósito.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ MD5 x SHA-1 x SHA-2 - Qual é o hash de criptografia mais seguro e como verificá-lo ]]>
                </title>
                <description>
                    <![CDATA[ O que é uma função hash? Uma função hash recebe um valor de entrada (como uma string, por exemplo) e retorna um valor de comprimento fixo. Uma função hash ideal tem as seguintes propriedades:  * É rápida [https://en.wikipedia.org/wiki/Hash_function#Efficiency] (em inglês)  * É capaz de retornar uma quantidade enorme ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/md5-x-sha-1-x-sha-2-qual-e-o-hash-de-criptografia-mais-seguro-e-como-verifica-lo/</link>
                <guid isPermaLink="false">6319bb04281b1606c8fcd50f</guid>
                
                    <category>
                        <![CDATA[ Funções de hash ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Glaucia Esppenchutz ]]>
                </dc:creator>
                <pubDate>Mon, 03 Oct 2022 21:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2022/09/Hash_function_long.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/md5-vs-sha-1-vs-sha-2-which-is-the-most-secure-encryption-hash-and-how-to-check-them/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">MD5 vs SHA-1 vs SHA-2 - Which is the Most Secure Encryption Hash and How to Check Them</a>
      </p><h1 id="o-que-uma-fun-o-hash"><strong>O que é uma função </strong><em><strong>hash</strong></em><strong>?</strong></h1><p>Uma função <em>hash</em> recebe um valor de entrada (como uma <em>string</em>, por exemplo) e retorna um valor de comprimento fixo. Uma função <em>hash</em> <strong>ideal</strong> tem as seguintes propriedades:</p><ul><li>É <a href="https://en.wikipedia.org/wiki/Hash_function#Efficiency">rápida</a> (em inglês)</li><li>É capaz de retornar uma quantidade enorme de valores <em>hash</em></li><li>Gera valores únicos de <em>hash</em> para cada entrada (sem colisões)</li><li>Gera diferentes de <em>hash</em> para valores de entrada similares</li><li>Os valores <em>hash</em> não tem padrão discernível em sua <a href="https://en.wikipedia.org/wiki/Hash_function#Uniformity">distribuição</a> (em inglês)</li></ul><p>Não existe uma função <em>hash</em> ideal, claro, mas cada uma procura operar o mais perto possível do ideal. Dado que (em grande parte) as funções de <em>hash</em> retornam valores de comprimento fixo e que o intervalo de valores é, portanto, restrito, essa restrição pode ser praticamente ignorada. O número de valores possíveis que podem ser retornados por uma função <em>hash</em> de 256 bits, por exemplo, é aproximadamente o mesmo número de <a href="https://nakamoto.com/hash-functions/#collision-resistance">átomos no universo</a> (em inglês).</p><p>Idealmente, uma função <em>hash</em> não retorna colisões, praticamente - dito isso, dois valores de entrada não geram <em>hashes</em> iguais. Isso é particularmente importante para <a href="https://pt.wikipedia.org/wiki/Fun%C3%A7%C3%A3o_hash_criptogr%C3%A1fica">funções <em>hash</em> de criptografia</a>: colisões de <em>hashes</em> são consideradas <a href="https://en.wikipedia.org/wiki/Collision_resistance">vulnerabilidades</a> (em inglês).</p><p>Por fim, uma função <em>hash</em> deve gerar valores imprevisíveis para cada valor de entrada. Por exemplo, observe as duas sentenças abaixo com valores similares:</p><pre><code>1. "The quick brown fox."
2. "The quick brown fax."
</code></pre><p>Podemos comparar os <a href="https://www.md5hashgenerator.com/">valores <em>hash</em> gerados em MD5</a> para cada uma das duas sentenças:</p><pre><code>1. 2e87284d245c2aae1c74fa4c50a74c77
2. c17b6e9b160cda0cf583e89ec7b7fc22
</code></pre><p>Dois valores diferentes foram gerados para duas sentenças similares, o que é uma propriedade muito útil para validação e para a criptografia. Isso é um corolário de uma <a href="https://en.wikipedia.org/wiki/Hash_function#Uniformity">distribuição</a> (em inglês): os valores <em>hash</em> de todas as entradas deveriam propagar de maneira uniforme e imprevisível em todo o intervalo de valores possíveis de <em>hash</em>.</p><h1 id="fun-es-hash-comuns"><strong>Funções <em>hash</em> comuns</strong></h1><p>Existem diversas funções <em>hash</em> amplamente usadas. Todas foram desenhadas por matemáticos e cientistas da computação. &nbsp;Ao longo de mais pesquisas, algumas mostraram ter alguns pontos fracos. Ainda assim, foram consideradas boas o suficiente para aplicações não criptográficas.</p><h2 id="md5"><strong><strong>MD5</strong></strong></h2><p>Uma função <em>hash</em> MD5 produz valores <em>hash</em> de 128 bits. Ela foi projetada para uso criptográfico, mas algumas vulnerabilidades foram descobertas no mesmo período e, por conta disso, passou a não ser mais recomendada. No entanto, ainda é usada para particionamento de banco de dados e somas de verificação de computação para validar transferências de arquivos. </p><h2 id="sha-1"><strong><strong>SHA-1</strong></strong></h2><p>SHA significa <em>Secure Hash Algorithm</em> (ou Algoritmo de <em>Hash</em> Seguro, em português). A primeira versão do algoritmo foi a SHA-1, seguida mais tarde pela SHA-2 (veja abaixo).</p><p>Enquanto o MD5 produz um <em>hash</em> de 128 bits, o SHA-1 gera um <em>hash</em> de 160 bits (20 bytes). Em um formato hexadecimal, isso seria um valor inteiro de 40 algarismos. Como o MD5, ele foi projetado para criptografia de aplicações, mas também foram encontradas vulnerabilidades. Hoje, é considerado tão pouco resistente a ataques quanto o MD5.</p><h2 id="sha-2"><strong><strong>SHA-2</strong></strong></h2><p>A segunda versão do SHA, chamada de SHA2, tem muitas variações. Provavelmente, uma das mais comuns é a SHA-256, cujo uso é recomendado pelo National Institute of Standards and Technology (NIST) no lugar do MD5 ou do SHA-1.</p><p>O algoritmo SHA-256 retorna um valor <em>hash</em> de 256 bits, ou 64 algarismos hexadecimais. Mesmo que ainda não seja perfeito, pesquisas recentes indicam que ele é consideravelmente mais seguro do que o MD5 ou o SHA-1.</p><p>Em termos de desempenho, o SHA-256 é entre 20 a 30% mais lento de calcular do que o MD5 ou o SHA-1.</p><h2 id="sha-3"><strong><strong>SHA-3</strong></strong></h2><p>Esse método <em>hash</em> foi desenvolvido no final de 2015 e ainda não foi visto em amplo uso. Seu algoritmo não se relaciona com seu predecessor, o SHA-2.</p><p>O algoritmo SHA3-256 é uma variante com aplicabilidade equivalente à do SHA-256 anterior, demorando um pouco mais para calcular do o SHA-2.</p><h1 id="usando-valores-hash-para-valida-o"><strong>Usando valores <em>hash</em> para validação</strong></h1><p>O uso típico de uma função <em>hash</em> é fazer verificações de validação. Um uso frequente é a validação de coleções de arquivos comprimidos, como arquivos do tipo .zip ou .tar.</p><p>Dado o arquivo e o seu valor de <em>hash</em> esperado (comumente referido como uma <a href="https://techterms.com/definition/checksum">soma de verificação</a> - ou <em>checksum</em>, em inglês), você pode realizar seu próprio cálculo de <em>hash</em> para validar se o arquivo que recebeu está completo ou corrompido.</p><p>Por exemplo, eu posso gerar uma soma de verificação de MD5 para um arquivo .tar a partir de um sistema Unix usando os seguintes comandos:</p><pre><code>tar cf - files | tee tarfile.tar | md5sum -
</code></pre><p>Para conseguir o <em>hash</em> MD5 para um arquivo no Windows, use o comando <a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/get-filehash?view=powershell-7">Get-FileHash</a> no PowerShell.</p><pre><code>Get-FileHash tarfile.tar -Algorithm MD5
</code></pre><p>A soma de verificação gerada pode ser exibida no local de download, próximo ao link de download. Para o receptor, uma vez que ele faz o download do arquivo, ele também pode fazer a validação de que está tudo correto executando o seguinte comando: </p><pre><code>echo '2e87284d245c2aae1c74fa4c50a74c77 tarfile.tar' | md5sum -c
</code></pre><p>onde <strong><strong>2e87284d245c2aae1c74fa4c50a74c77</strong> </strong>é o valor postado da soma de verificação. Uma execução com sucesso do comando acima gerará um <em>status</em> de "OK" como esse:</p><pre><code>echo '2e87284d245c2aae1c74fa4c50a74c77 tarfile.tar' | md5sum -ctarfile.tar: OK</code></pre> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Tutorial de Joins em SQL: Cross Join, Full Outer Join, Inner Join, Left Join e Right Join ]]>
                </title>
                <description>
                    <![CDATA[ As joins (ou junções, no português) do SQL permitem que nossos sistemas de gerenciamento de banco de dados sejam, bem, relacionais. Joins nos permitem reconstruir nossas tabelas separadas dentro do banco de dados em relacionamentos poderosos para nossas aplicações. Neste artigo, vamos olhar para cada tipo diferente de join no ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/tutorial-de-joins-em-sql-cross-join-full-outer-join-inner-join-left-join-e-right-join/</link>
                <guid isPermaLink="false">62b9ce78a3520206e79ceb2b</guid>
                
                    <category>
                        <![CDATA[ SQL ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Glaucia Esppenchutz ]]>
                </dc:creator>
                <pubDate>Fri, 22 Jul 2022 21:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/5f9c98fc740569d1a4ca1d31.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/sql-joins-tutorial/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">SQL Joins Tutorial: Cross Join, Full Outer Join, Inner Join, Left Join, and Right Join.</a>
      </p><p>As <em>joins</em> (ou junções, no português)<em> </em>do SQL permitem que nossos sistemas de gerenciamento de banco de dados sejam, bem, <em>relacionais.</em></p><p><em>Joins</em> nos permitem reconstruir nossas tabelas separadas dentro do banco de dados em relacionamentos poderosos para nossas aplicações.</p><p>Neste artigo, vamos olhar para cada tipo diferente de <em>join</em> no SQL e aprender como usá-los.</p><p>Sobre o que vamos tratar neste artigo:</p><ul><li>O que é uma <em>join</em>? </li><li>Como configurar nosso banco de dados</li><li><code>CROSS JOIN</code></li><li>Como configurar nossos dados de exemplo (diretores e filmes)</li><li><code>FULL OUTER JOIN</code></li><li><code>INNER JOIN</code></li><li><code>LEFT JOIN</code> / <code>RIGHT JOIN</code></li><li>Como filtrar dados com <a href="https://www.freecodecamp.org/news/sql-joins-tutorial/#filtering-using-left-join"><code>LEFT JOIN</code></a></li><li>Múltiplas <em>joins</em></li><li><em>Joins</em> com condições extras</li><li>A realidade sobre escrever <em>queries </em>(ou buscas, no português) &nbsp;com <em>joins</em></li></ul><p><em>(Alerta de spoiler: abordaremos cinco tipos diferentes– mas você precisará saber apenas dois deles!)</em></p><h2 id="o-que-uma-join"><strong>O que é uma join?</strong></h2><p>Uma <em>join</em> é uma operação que combina duas ou mais linhas em uma só linha.</p><p>Essas linhas normalmente vêm de duas tabelas diferentes, embora não precisem ser.</p><p>Antes de olharmos como escrever uma <em>join</em> propriamente dita, vamos olhar para o que seria o resultado de uma <em>join</em>.</p><p>Vamos pegar o exemplo de um sistema que armazena informações sobre usuários e seus endereços.</p><p>As linhas da tabela que armazena as informações dos usuários se parece com isso:</p><pre><code> id |     name     |        email        | age
----+--------------+---------------------+-----
  1 | John Smith   | johnsmith@gmail.com |  25
  2 | Jane Doe     | janedoe@Gmail.com   |  28
  3 | Xavier Wills | xavier@wills.io     |  3
...
(7 rows)</code></pre><p>E as linhas da tabela que armazena informações sobre os endereços seria desse jeito:</p><pre><code> id |      street       |     city      | state | user_id
----+-------------------+---------------+-------+---------
  1 | 1234 Main Street  | Oklahoma City | OK    |       1
  2 | 4444 Broadway Ave | Oklahoma City | OK    |       2
  3 | 5678 Party Ln     | Tulsa         | OK    |       3
(3 rows)</code></pre><p>Poderíamos escrever <em>queries </em>para buscar separadamente tanto as informações do usuário como seu endereço – mas, idealmente, poderíamos escrever <em>apenas uma query</em> para retornar todos os usuários e seus endereços no mesmo resultado.</p><p>E é exatamente isso que a <em>join</em> nos permite!</p><p>Veremos como escrever essas junções em breve, mas, se juntarmos nossas informações de usuário com as de endereço, teremos este resultado:</p><pre><code> id |     name     |        email        | age | id |      street       |     city      | state | user_id
----+--------------+---------------------+-----+----+-------------------+---------------+-------+---------
  1 | John Smith   | johnsmith@gmail.com |  25 |  1 | 1234 Main Street  | Oklahoma City | OK    |       1
  2 | Jane Doe     | janedoe@Gmail.com   |  28 |  2 | 4444 Broadway Ave | Oklahoma City | OK    |       2
  3 | Xavier Wills | xavier@wills.io     |  35 |  3 | 5678 Party Ln     | Tulsa         | OK    |       3
(3 rows)
</code></pre><p>Aqui, podemos ver todos os nossos usuários e seus respectivos endereços em um único resultado.</p><p>Além de produzir resultados combinados, outro uso importante das <em>joins</em> é retirar informações adicionais da nossa busca, que também podem ser filtrados. </p><p>Por exemplo, se quisermos enviar uma correspondência física para nossos usuários que moram na cidade de Oklahoma, podemos usar essa <em>join</em> e definir um filtro baseado na coluna <code>city</code>.</p><p>Agora que sabemos o propósito das <em>joins</em>– vamos escrever algumas!</p><h2 id="como-configurar-nosso-banco-de-dados"><strong>Como configurar nosso banco de dados</strong></h2><p>Antes de começarmos a escrever nossas <em>queries</em>, precisamos configurar nosso banco de dados.</p><p>Para os nossos exemplos, usaremos o PostgreSQL, mas a escrita das <em>queries </em>e os conceitos mostrados aqui podem ser facilmente traduzidos para qualquer outro sistema de banco de dados moderno (como o MySQL, o SQL Server etc.).</p><p>Para trabalhar com um banco PostgreSQL, usamos o <code><a href="https://www.postgresql.org/docs/current/app-psql.html">psql</a></code> – a linha de comando interativa do PostgreSQL. Se você tiver algum outro <em>client</em> (ou serviço, em português) com o qual esteja acostumado a trabalhar, não tem problema.</p><p>Vamos começar criando nosso banco de dados. Com o PostegreSQL já <a href="https://www.postgresql.org/download/">instalado</a>, podemos executar o comando <code>createdb &lt;nome-do-banco-da-dados&gt;</code> em nosso terminal para criar um banco de dados. Vou chamar o meu de <code>fcc</code>:</p><pre><code class="language-bash">$ createdb fcc
</code></pre><p>Vamos iniciar o console interativo usando o comando &nbsp;<code>psql</code> e conectar o banco de dados que criamos usando <code>\c &lt;nome-do-banco-de-dados&gt;</code>:</p><pre><code class="language-bash">$ psql
psql (11.5)
Type "help" for help.

john=# \c fcc
You are now connected to database "fcc" as user "john".
fcc=#
</code></pre><blockquote><em><strong>Observação</strong>: eu limpei o resultado do <code>psql</code> nesses exemplos para tornar mais fácil a leitura. Então, não se preocupe se o seu retorno não for exatamente igual ao que está vendo no seu terminal.</em></blockquote><p>Eu aconselho você a seguir os exemplos e executar as <em>queries</em> por conta própria. Desse modo, você aprenderá e se lembrará com mais facilidade, trabalhando nos exemplos em vez de apenas ler sobre eles.</p><p>Agora, vamos às <em>joins</em>!</p><h2 id="cross-join"><strong><strong><code>CROSS JOIN</code></strong></strong></h2><p>O tipo mais simples de <em>join</em> que podemos fazer é a <code>CROSS JOIN</code>, ou "<em>produto cartesiano</em>".</p><p>Essa <em>join</em> pega cada linha de uma tabela e a combina com cada linha da outra tabela.</p><p>Se tivéssemos duas listas – uma contendo <code>1, 2, 3</code> e a outra contendo <code>A, B, C</code> — o produto cartesiano das duas seria:</p><pre><code>1A, 1B, 1C
2A, 2B, 2C
3A, 3B, 3C
</code></pre><p>Cada valor da primeira lista é emparelhado com cada valor da segunda lista.</p><p>Vamos escrever esse exemplo simples em uma <em>query</em> do SQL.</p><p>Primeiro, vamos criar duas tabelas simples e inserir alguns dados:</p><pre><code class="language-sql">CREATE TABLE letters(
  letter TEXT
);

INSERT INTO letters(letter) VALUES ('A'), ('B'), ('C');

CREATE TABLE numbers(
  number TEXT
);

INSERT INTO numbers(number) VALUES (1), (2), (3);
</code></pre><p>Nossas duas tabelas, <code>letters</code> e <code>numbers</code>, tem apenas uma coluna: um simples campo de texto. </p><p>Agora, vamos juntá-las com um <code>CROSS JOIN</code>:</p><pre><code class="language-sql">SELECT *
FROM letters
CROSS JOIN numbers;
</code></pre><pre><code> letter | number
--------+--------
 A      | 1
 A      | 2
 A      | 3
 B      | 1
 B      | 2
 B      | 3
 C      | 1
 C      | 2
 C      | 3
(9 rows)

</code></pre><p>Esse é o tipo mais simples de <em>join</em> que podemos fazer – mesmo nesse exemplo simples, no entanto, podemos ver a <em>join</em> em funcionamento: as duas linhas separadas (uma de <code>letters</code> e outra de <code>numbers</code>) foram combinadas para formar uma única linha.</p><p>Embora esse tipo de <em>join</em> seja frequentemente discutido como sendo apenas um exemplo acadêmico, ele tem um bom caso de uso: cobrir intervalos de datas.</p><h3 id="cross-join-com-intervalos-de-datas"><strong><strong><code>CROSS JOIN</code> </strong>com intervalos de datas</strong></h3><p>Um bom caso de uso do <code>CROSS JOIN</code> é pegar cada linha de uma tabela e aplicar para cada dia de um intervalo de data específico.</p><p>Digamos, por exemplo, que estávamos criando uma aplicação que monitora atividades diárias – coisas como escovar os dentes, tomar o café da manhã ou tomar banho.</p><p>Se você quiser gerar um registro <em>para cada tarefa</em> e <em>para cada dia</em> da semana passada, você poderia usar a <code>CROSS JOIN</code> em um intervalo de datas.</p><p>Para criar esse intervalo, podemos usar a função <code><a href="https://www.postgresql.org/docs/current/functions-srf.html">generate_series</a></code>:</p><pre><code class="language-sql">SELECT generate_series(
  (CURRENT_DATE - INTERVAL '5 day'),
  CURRENT_DATE,
  INTERVAL '1 day'
)::DATE AS day;
</code></pre><p>A função <code><a href="https://www.postgresql.org/docs/current/functions-srf.html">generate_series</a></code> recebe três parâmetros:</p><p>O primeiro parâmetro é o valor de início. Nesse exemplo, usamos <code>CURRENT_DATE - INTERVAL '5 day'</code>. Isso retorna o dia atual menos 5 dias – ou "cinco dias atrás".</p><p>O segundo parâmetro é a data atual (<code>CURRENT_DATE</code>).</p><p>O terceiro parâmetro é o "intervalo" – ou o quanto nós queremos incrementar em cada valor por vez. Já que essas são tarefas diárias, vamos usar o intervalo de um dia (<code>INTERVAL '1 day'</code>).</p><p>Juntando tudo, isso gera uma série de datas começando de cinco dias atrás, terminando hoje e sendo incrementando um dia por vez.</p><p>Finalmente, removemos a parte do tempo, lançando a saída desses valores para uma data usando <code>::DATE</code>, e atribuímos um nome à coluna usando <code>AS day</code> para fazer com que o retorno seja um pouco melhor.</p><p>O resultado dessa <em>query</em> são os últimos cinco dias mais o dia de hoje:</p><pre><code>    day
------------
 2020-08-19
 2020-08-20
 2020-08-21
 2020-08-22
 2020-08-23
 2020-08-24
(6 rows)
</code></pre><p>Voltando ao nosso exemplo de tarefas diárias, vamos criar uma tabela simples para conter as tarefas que queremos completar e inserir algumas tarefas:</p><pre><code class="language-sql">CREATE TABLE tasks(
  name TEXT
);

INSERT INTO tasks(name) VALUES
('Escovar os dentes'),
('Tomar o café da manhã'),
('Tomar banho'),
('Vestir-se');
</code></pre><p>Nossa tabela <code>tasks</code> tem apenas uma coluna, <code>name</code>, Nela, nós inserimos quatro tarefas.</p><p>Agora, vamos usar a <code>CROSS JOIN</code> nas nossas tarefas e na <em>query</em> que cria o intervalo de datas:</p><pre><code class="language-sql">SELECT
  tasks.name,
  dates.day
FROM tasks
CROSS JOIN
(
  SELECT generate_series(
    (CURRENT_DATE - INTERVAL '5 day'),
    CURRENT_DATE,
    INTERVAL '1 day'
  )::DATE	AS day
) AS dates
</code></pre><p>(Já que nossa <em>query</em> de produção de dados não é exatamente uma tabela, nós podemos escrevê-la como uma <em>subquery</em>.)</p><p>A partir dessa <em>query,</em> retornamos o nome das tarefas e o dia. O resultado terá essa aparência:</p><pre><code>     name              |    day
---------------+-------------------
 Escovar os dentes     | 2020-08-19
 Escovar os dentes     | 2020-08-20
 Escovar os dentes     | 2020-08-21
 Escovar os dentes     | 2020-08-22
 Escovar os dentes     | 2020-08-23
 Escovar os dentes     | 2020-08-24
 Tomar o café da manhã | 2020-08-19
 Tomar o café da manhã | 2020-08-20
 Tomar o café da manhã | 2020-08-21
 Tomar o café da manhã | 2020-08-22
 ...
 (24 rows)
</code></pre><p>Como esperávamos, nós recebemos uma linha para cada tarefa para cada dia em nosso intervalo de datas. </p><p>A <code>CROSS JOIN</code> é a <em>join</em> mais simples que podemos fazer, mas, para ver os outros tipos, vamos precisar de um exemplo mais realista de configuração de tabela:</p><h2 id="como-configurar-nossos-dados-de-exemplo-diretores-e-filmes-">Como configurar nossos dados de exemplo (diretores e filmes)</h2><p>Para ilustrar os próximos tipos de <em>join</em>, vamos usar o exemplo de <em><em>movies</em> e <em>movie directors</em> </em>(filmes e diretores de filmes, em português)<em><em>.</em></em></p><p>Nessa situação, o filme tem apenas um diretor, mas um filme não necessariamente precisa ter um diretor associado – imagine que um novo filme foi anunciado mas a escolha do diretor ainda não foi feita ou confirmada.</p><p>Nossa tabela <code>directors</code> armazenará o nome de cada diretor, enquanto a tabela <code>movies</code> armazenará o nome de cada filme, assim como a referência do diretor associado (caso haja uma).</p><p>Vamos criar as duas tabelas e inserir alguns dados nelas:</p><pre><code class="language-sql">CREATE TABLE directors(
  id SERIAL PRIMARY KEY,
  name TEXT NOT NULL
);

INSERT INTO directors(name) VALUES
('John Smith'),
('Jane Doe'),
('Xavier Wills')
('Bev Scott'),
('Bree Jensen');

CREATE TABLE movies(
  id SERIAL PRIMARY KEY,
  name TEXT NOT NULL,
  director_id INTEGER REFERENCES directors 
);

INSERT INTO movies(name, director_id) VALUES
('Movie 1', 1),
('Movie 2', 1),
('Movie 3', 2),
('Movie 4', NULL),
('Movie 5', NULL);
</code></pre><p>Temos cinco diretores, cinco filmes e três desses filmes tem diretores associados a eles. O diretor com o ID 1 tem dois filmes, enquanto o diretor ID 2 tem um.</p><h2 id="full-outer-join"><strong><strong><code>FULL OUTER JOIN</code></strong></strong></h2><p>Agora que temos alguns dados para trabalhar, vamos olhar a <code>FULL OUTER JOIN</code>.</p><p>A <code>FULL OUTER JOIN</code> tem algumas semelhanças com a <code>CROSS JOIN</code>, mas algumas diferenças importantes.</p><p>A primeira diferença é que uma <code>FULL OUTER JOIN</code> precisa de uma <strong>condição para fazer a <em>join</em>.</strong></p><p>A condição da <em>join</em> especifica como duas linhas de duas tabelas diferentes estão relacionadas uma com a outra, bem como o critério a ser usado para juntá-las.</p><p>No nosso exemplo, nossa tabela <code>movies</code> tem uma referência aos diretores através da coluna <code>director_id</code>. Essa coluna corresponde à coluna <code>id</code> da tabela <code>directors</code>. Essas são as duas colunas que usaremos como condição para a <em>join</em>.</p><p>Escrevemos essa <em>join</em> entre as duas tabelas desta forma:</p><pre><code class="language-sql">SELECT *
FROM movies
FULL OUTER JOIN directors
  ON directors.id = movies.director_id;
</code></pre><p>Observe a condição da <em>join</em> especificada, que corresponde o filme ao seu diretor : <code>ON movies.director_id = directors.id</code>.</p><p>Nossos resultados vão se parecer com um produto cartesiano estranho:</p><pre><code>  id  |  name   | director_id |  id  |     name
------+---------+-------------+------+--------------
    1 | Movie 1 |           1 |    1 | John Smith
    2 | Movie 2 |           1 |    1 | John Smith
    3 | Movie 3 |           2 |    2 | Jane Doe
    4 | Movie 4 |        NULL | NULL | NULL
    5 | Movie 5 |        NULL | NULL | NULL
 NULL | NULL    |        NULL |    5 | Bree Jensen
 NULL | NULL    |        NULL |    4 | Bev Scott
 NULL | NULL    |        NULL |    3 | Xavier Wills
(8 rows)
</code></pre><p>As primeiras linhas que veremos são aquelas onde o filme tem um diretor e que nossa condição de <em>join</em> avaliou como verdadeira.</p><p>No entanto, após essas linhas, vemos algumas linhas restantes de cada tabela – mas com valores <code>NULL</code>, onde a outra tabela não teve uma correspondência.</p><blockquote><strong>Observação</strong>: se você não está familiarizado com valores do tipo <code>NULL</code>, pode <a href="https://www.freecodecamp.org/news/sql-operators-tutorial/#dealing-with-missing-data-null-">veja esta explicação</a> (em inglês) no tutorial de operadores SQL</blockquote><p>Também vemos outra diferença entre a <code>CROSS JOIN</code> e a <code>FULL OUTER JOIN</code> aqui. A <code>FULL OUTER JOIN</code> retorna uma linha distinta em cada linha– diferentemente da <code>CROSS JOIN</code>, que retorna múltiplas. </p><h2 id="inner-join"><strong><strong><code>INNER JOIN</code></strong></strong></h2><p>O próximo tipo de <em>join</em>, a <code>INNER JOIN</code>, é mais comumente utilizado do que todos os outros.</p><p>Uma <em>inner join</em> <strong>retorna apenas as linhas onde a condição de <em>join</em> é verdadeira.</strong></p><p>No nosso exemplo, uma <em>inner join</em> entre nossas tabelas de &nbsp;<code>movies</code> e <code>directors</code> retornaria apenas registros onde o filme possui um diretor atribuído.</p><p>A sintaxe é basicamente a mesma que vimos antes:</p><pre><code class="language-sql">SELECT *
FROM movies
INNER JOIN directors
  ON directors.id = movies.director_id;
</code></pre><p>Nosso resultado mostra apenas os três filmes que possuem um diretor.</p><pre><code> id |  name   | director_id | id |    name
----+---------+-------------+----+------------
  1 | Movie 1 |           1 |  1 | John Smith
  2 | Movie 2 |           1 |  1 | John Smith
  3 | Movie 3 |           2 |  2 | Jane Doe
(3 rows)
</code></pre><p>Já que uma <em>inner join</em> inclui apenas as linhas que tenham uma condição de <em>join</em> que a corresponda, a <em>ordem de junção das duas tabelas não fará diferença.</em></p><p>Se invertermos a ordem das tabelas, a <em>query</em> terá o mesmo resultado:</p><pre><code class="language-sql">SELECT *
FROM directors
INNER JOIN movies
  ON movies.director_id = directors.id;
</code></pre><pre><code> id |    name    | id |  name   | director_id
----+------------+----+---------+-------------
  1 | John Smith |  1 | Movie 1 |           1
  1 | John Smith |  2 | Movie 2 |           1
  2 | Jane Doe   |  3 | Movie 3 |           2
(3 rows)
</code></pre><p>Como listamos a tabela de <code>directors</code> primeiro nessa <em>query</em> e selecionamos todas as colunas (<code>SELECT *</code>), vemos a coluna de <code>directors</code> primeiro e depois as outras colunas de <code>movies</code> - o resultado dos dados, no entanto, é o mesmo.</p><p>Essa é uma propriedade muito útil das <em>inner joins</em>, mas não é uma realidade de todos os outros tipos de <em>join</em>– como veremos no nosso próximo exemplo.</p><h2 id="left-join-right-join"><strong><strong><code>LEFT JOIN</code>/<code>RIGHT JOIN</code></strong></strong></h2><p>Os próximos dois tipos de <em>join</em> usam um modificador (<code>LEFT</code> ou <code>RIGHT</code>) que afeta qual dado de tabela será incluído no resultado final.</p><blockquote><strong>Observação</strong>: a <code>LEFT JOIN</code> e a <code>RIGHT JOIN</code> também podem ser referidas como <code>LEFT OUTER JOIN</code> e <code>RIGHT OUTER JOIN</code>.</blockquote><p>Essas <em>joins</em> são usadas em <em>queries</em> onde queremos retornar todos os dados da tabela específica e,<em> caso existam</em>, os dados correspondentes da outra tabela. </p><p>Se os dados correspondentes não existirem, ainda teremos os dados da tabela primária.</p><p>Seria como uma busca por uma informação específica e um bônus caso a informação de bônus exista.</p><p>Isso será simples de entender com um exemplo. Vamos encontrar todos os filmes e seus diretores, mas não vamos nos importar com o fato de eles terem um diretor ou não – esse é o bônus:</p><pre><code class="language-sql">SELECT *
FROM movies
LEFT JOIN directors
  ON directors.id = movies.director_id;
</code></pre><p>A <em>query</em> segue o mesmo padrão que vimos antes – apenas especificamos a <em>join</em> como <code>LEFT JOIN</code>.</p><p>Nesse exemplo, a tabela <code>movies</code> é a tabela <em>left</em> (ou da esquerda, em português).</p><p>Se escrevermos a <em>query</em> em uma linha, fica um pouco mais fácil de ver:</p><pre><code class="language-sql">... FROM movies LEFT JOIN directors ...
</code></pre><p><strong>Uma <em>left join</em> retorna todos os dados da tabela da esquerda.</strong></p><p>Uma <em>left join</em> retorna também todas linhas da tabela da direita (a outra tabela, no caso) <strong>que correspondam à condição da <em>join</em>.</strong></p><p>As colunas da tabela da direita <strong>que não correspondam à condição da <em>join</em> serão retornadas como <code>NULL</code>.</strong></p><pre><code> id |  name   | director_id |  id  |    name
----+---------+-------------+------+------------
  1 | Movie 1 |           1 |    1 | John Smith
  2 | Movie 2 |           1 |    1 | John Smith
  3 | Movie 3 |           2 |    2 | Jane Doe
  4 | Movie 4 |        NULL | NULL | NULL
  5 | Movie 5 |        NULL | NULL | NULL
(5 rows)
</code></pre><p>Olhando para os resultados, &nbsp;podemos ver o porquê de esse tipo de <em>join</em> ser útil para uma busca que "<em>traga tudo disto e, caso exista, um pouco daquilo</em>".</p><h3 id="right-join"><strong><strong><code>RIGHT JOIN</code></strong></strong></h3><p>A <code>RIGHT JOIN</code> funciona exatamente como a <code>LEFT JOIN</code> – exceto que as regras agora são inversas para a ordem das tabelas.</p><p>Em uma <em>right join</em>, todas as linhas da tabela da direita são retornadas. A tabela da esquerda é retornada com base na condição da <em>join</em>.</p><p>Vamos usar a mesma query que vimos acima e substituir a <code>LEFT JOIN</code> por <code>RIGHT JOIN</code>:</p><pre><code class="language-sql">SELECT *
FROM movies
RIGHT JOIN directors
  ON directors.id = movies.director_id;
</code></pre><pre><code>  id  |  name   | director_id | id |     name
------+---------+-------------+----+--------------
    1 | Movie 1 |           1 |  1 | John Smith
    2 | Movie 2 |           1 |  1 | John Smith
    3 | Movie 3 |           2 |  2 | Jane Doe
 NULL | NULL    |        NULL |  5 | Bree Jensen
 NULL | NULL    |        NULL |  4 | Bev Scott
 NULL | NULL    |        NULL |  3 | Xavier Wills
(6 rows)
</code></pre><p>Nosso resultado agora são todas as linhas de <code>directors</code> e, caso existam, os dados de <code>movies</code>.</p><p>Tudo &nbsp;o que fizemos foi trocar qual tabela é considerada primária – a tabela da qual queremos ver todos os dados sem considerar se temos dados associados.</p><h3 id="left-join-right-join-na-produ-o-de-aplica-es"><strong><strong><code>LEFT JOIN</code>/<code>RIGHT JOIN</code> </strong>na produção de aplicações</strong></h3><p>Na criação de uma aplicação, eu utilizo apenas <code>LEFT JOIN</code> e nunca uso <code>RIGHT JOIN</code>.</p><p>Eu faço isso porque, na minha opinião, um <code>LEFT JOIN</code> torna a <em>query</em> muito mais fácil de ser lida e entendida.</p><p>Quando estou escrevendo uma <em>query,</em> gosto de começar a pensar em um resultado "base", digamos todos os dados de filmes, e então trazer (ou retirar) os grupos de informações dessa base.</p><p>Como eu gosto de começar com uma base, a <code>LEFT JOIN</code> se encaixa bem nessa linha de raciocínio. Eu quero todas as linhas da minha tabela base (a tabela da esquerda) e, condicionalmente, as da tabela da direita. </p><p>Na prática, eu não acho que já tenha visto uma <code>RIGHT JOIN</code> em uma aplicação em produção. Entenda, não tem nada de errado com uma <code>RIGHT JOIN</code> - eu apenas acho que deixa a <em>query</em> mais difícil de entender.</p><h3 id="reescrevendo-o-right-join"><strong>Reescrevendo o<strong> <code>RIGHT JOIN</code></strong></strong></h3><p>Se quisermos virar nosso cenário acima, ao invés de retornar todos os diretores e condicionalmente seus filmes, podemos facilmente reescrever a <code>RIGHT JOIN</code> para uma <code>LEFT JOIN</code>.</p><p>Tudo que precisamos fazer é trocar a ordem das tabelas da <em>query</em> e mudar a palavra <code>RIGHT</code> para <code>LEFT</code>:</p><pre><code class="language-sql">SELECT *
FROM directors
LEFT JOIN movies
  ON movies.director_id = directors.id;
</code></pre><blockquote>Observação: eu gosto de colocar a tabela que será adicionada (a da direita– no exemplo acima, de <code>movies</code>) primeiro na condição da join (<code>ON movies.director_id = ...</code>) – mas essa é apenas minha preferencia pessoal.</blockquote><h2 id="como-filtrar-dados-com-left-join"><strong>Como filtrar dados com<strong> <code>LEFT JOIN</code></strong></strong></h2><p>Existem dois casos de uso para a <code>LEFT JOIN</code> (ou a <code>RIGHT JOIN</code>).</p><p>O primeiro, nós já vimos: retornar todas as linhas de uma tabela e, condicionalmente, as da outra.</p><p>O segundo caso de uso é para retornar as linhas da primeira tabela onde os <strong>dados da segunda tabela não estão presentes.</strong></p><p>O cenário tem essa aparência: <em>encontre todos os diretores que não pertencem a nenhum filme.</em></p><p>Para fazer isso, vamos começar com uma <code>LEFT JOIN</code> e nossa tabela <code>directors</code> será a primária ou a tabela da esquerda: </p><pre><code class="language-sql">SELECT *
FROM directors
LEFT JOIN movies
  ON movies.director_id = directors.id;
</code></pre><p>Para um diretor que não pertence a nenhum filme, as colunas da tabela <code>movies</code> são <code>NULL</code>:</p><pre><code> id |     name     |  id  |  name   | director_id
----+--------------+------+---------+-------------
  1 | John Smith   |    1 | Movie 1 |           1
  1 | John Smith   |    2 | Movie 2 |           1
  2 | Jane Doe     |    3 | Movie 3 |           2
  5 | Bree Jensen  | NULL | NULL    |        NULL
  4 | Bev Scott    | NULL | NULL    |        NULL
  3 | Xavier Wills | NULL | NULL    |        NULL
(6 rows)
</code></pre><p>No nosso exemplo, os diretores com ID 3, 4 e 5 não pertencem a nenhum filme.</p><p>Para filtrar nosso resultado para apenas essas linhas, podemos adicionar uma cláusula <code>WHERE</code> que retornará apenas os filmes onde os dados são <code>NULL</code>:</p><pre><code class="language-sql">SELECT *
FROM directors
LEFT JOIN movies
  ON movies.director_id = directors.id
WHERE movies.id IS NULL;
</code></pre><pre><code> id |     name     |  id  | name | director_id
----+--------------+------+------+-------------
  5 | Bree Jensen  | NULL | NULL |        NULL
  4 | Bev Scott    | NULL | NULL |        NULL
  3 | Xavier Wills | NULL | NULL |        NULL
(3 rows)
</code></pre><p>E aqui estão nossos três filmes sem diretor!</p><p>É comum usar a coluna <code>id</code> da tabela para filtrar os dados (<code>WHERE movies.id IS NULL</code>), mas todas as colunas da tabela <code>movies</code> são <code>NULL</code> - nesse caso, qualquer uma funcionaria.</p><p>Já que sabemos que todas as colunas de <code>movies</code> seriam <code>NULL</code>. Na <em>query</em> acima, poderíamos escrever apenas <code>SELECT directors.*</code> ao invés de <code>SELECT *</code> para retornar todas as informações dos diretores.</p><h3 id="usando-a-left-join-para-encontrar-dados-correspondentes"><strong>Usando a <strong><code>LEFT JOIN</code></strong> para encontrar dados correspondentes<strong> </strong></strong></h3><p>Na nossa <em>query</em> anterior, encontramos diretores que <em>não pertenciam</em> a nenhum filme.</p><p>Usando a mesma estrutura, podemos encontrar os diretores que <em>pertencem </em>a algum filme mudando apenas a nossa condição <code>WHERE</code> para buscar as linhas onde os dados do filme <em>não são</em> <code>NULL</code>:</p><pre><code class="language-sql">SELECT *
FROM directors
LEFT JOIN movies
  ON movies.director_id = directors.id
WHERE movies.id IS NOT NULL;
</code></pre><pre><code> id |    name    | id |  name   | director_id
----+------------+----+---------+-------------
  1 | John Smith |  1 | Movie 1 |           1
  1 | John Smith |  2 | Movie 2 |           1
  2 | Jane Doe   |  3 | Movie 3 |           2
(3 rows)
</code></pre><p>Pode até parecer prático, mas, na verdade, nós implementamos novamente a <code>INNER JOIN</code>!</p><h2 id="m-ltiplas-joins"><strong>Múltiplas<strong> joins</strong></strong></h2><p>Nós já vimos como combinar duas tabelas, mas e se quisermos fazer múltiplas <em>joins</em> em uma linha?</p><p>Na verdade, é bastante simples, mas para ilustrar isso vamos precisar de uma terceira tabela: <code>tickets</code>.</p><p>Essa tabela representará os ingressos vendidos por filme:</p><pre><code class="language-sql">CREATE TABLE tickets(
  id SERIAL PRIMARY KEY,
  movie_id INTEGER REFERENCES movies NOT NULL
);

INSERT INTO tickets(movie_id) VALUES (1), (1), (3);
</code></pre><p>A tabela <code>tickets</code> &nbsp;tem apenas um <code>id</code> e a referência a um filme: <code>movie_id</code>.</p><p>Nós também inserimos dois ingressos vendidos para o filme ID 1, e um ingresso vendido para o filme ID 3.</p><p>Agora, vamos combinar <code>directors</code> com <code>movies</code> — e, então, <code>movies</code> com <code>tickets</code>!</p><pre><code class="language-sql">SELECT *
FROM directors
INNER JOIN movies
  ON movies.director_id = directors.id
INNER JOIN tickets
  ON tickets.movie_id = movies.id;
</code></pre><p>Já que ambas são <em>inner joins</em>, a ordem em que nós escrevemos a <em>join</em> não importa. Poderíamos ter começado com <code>tickets</code> e, então, adicionado <code>movies</code> e finalizado com a adição de <code>directors</code>.</p><p>Novamente, tudo se resume a forma na qual você está tentando consultar e o que faz a busca mais compreensível.</p><p>No nosso resultado, vamos perceber que reduzimos ainda mais as linhas retornadas:</p><pre><code> id |    name    | id |  name   | director_id | id | movie_id
----+------------+----+---------+-------------+----+----------
  1 | John Smith |  1 | Movie 1 |           1 |  1 |        1
  1 | John Smith |  1 | Movie 1 |           1 |  2 |        1
  2 | Jane Doe   |  3 | Movie 3 |           2 |  3 |        3
(3 rows)
</code></pre><p>Isso faz sentido porque adicionamos outra <code>INNER JOIN</code>. Na prática, ela adiciona uma outra condicional do tipo "AND" na nossa busca.</p><p>Nossa <em>query</em>, essencialmente, diz: <em>"retorne todos os diretores que pertencem a filmes <strong>que também tenham</strong> ingressos vendidos"</em></p><p>Se, ao invés disso, quiséssemos encontrar diretores que pertencem a filmes <em>que talvez não tenham ingressos vendidos ainda</em>, poderíamos substituir nossa <code>INNER JOIN</code> por uma <code>LEFT JOIN</code>:</p><pre><code class="language-sql">SELECT *
FROM directors
JOIN movies
  ON movies.director_id = directors.id
LEFT JOIN tickets
  ON tickets.movie_id = movies.id;
</code></pre><p>Podemos ver que <code>Movie 2</code> agora volta ao nosso conjunto de resultados:</p><pre><code> id |    name    | id |  name   | director_id |  id  | movie_id
----+------------+----+---------+-------------+------+----------
  1 | John Smith |  1 | Movie 1 |           1 |    1 |        1
  1 | John Smith |  1 | Movie 1 |           1 |    2 |        1
  2 | Jane Doe   |  3 | Movie 3 |           2 |    3 |        3
  1 | John Smith |  2 | Movie 2 |           1 | NULL |     NULL
(4 rows)
</code></pre><p>Esse filme ainda não teve nenhum ingresso vendido, mas foi excluído anteriormente dos resultados por conta da <code>INNER JOIN</code>.</p><p>Vou deixar isso como um <em>Exercício para o Leitor™</em>. Como você encontraria os diretores que pertencem a algum filme e <strong>não tem</strong> nenhum ingresso vendido?</p><h3 id="a-ordem-de-execu-o-das-joins"><strong>A ordem de execução das <em>j<strong>oin</strong>s</em></strong></h3><p>No fim, realmente não ligamos para a ordem como as <em>joins</em> são executadas.</p><p>Uma das diferenças principais entre o SQL e outras linguagens de programação modernas é o fato de o SQL ser uma linguagem <strong>declarativa</strong>.</p><p>Isso significa que especificamos o que queremos, mas não especificamos os detalhes da execução – esses detalhes são deixados para o planejador de buscas da base de dados. Especificamos as <em>joins</em> que queremos e as condições e o planejador do banco de dados cuida do resto.</p><p>Na realidade, porém, o banco de dados não está juntando três tabelas ao mesmo tempo. Ao invés disso, ele provavelmente unirá as duas primeiras tabelas em um resultado intermediário e, então, usará esse resultado para unir a terceira tabela.</p><p>(<strong>Observação</strong>: essa é uma explicação bastante simplificada.)</p><p>Então, como estamos trabalhando em buscas com várias <em>joins</em>, podemos simplesmente pensar que são <em>joins</em> entre duas tabelas – mesmo que uma delas seja bastante grande.</p><h2 id="joins-com-condi-es-extras"><strong><strong><em>Joins </em></strong>com condições extras</strong></h2><p>O último tópico de que trataremos são as <em>joins</em> com condições extras.</p><p>Da mesma forma que ocorre com a cláusula <code>WHERE</code>, podemos adicionar quantas condicionais quisermos as condições da <em>join</em>.</p><p>Por exemplo, se quisermos encontrar os filmes cujos diretores não tenham o nome "John Smith", podemos adicionar uma condição extra à nossa <em>join</em> com um <code>AND</code>:</p><pre><code class="language-sql">SELECT *
FROM movies
INNER JOIN directors
  ON directors.id = movies.director_id
  AND directors.name &lt;&gt; 'John Smith';</code></pre><p>Podemos usar qualquer operador onde colocaríamos uma cláusula <code>WHERE</code> nessa condicional da <em>join</em>.</p><p>Podemos também ter o mesmo resultado dessa busca se colocarmos a condicional com a cláusula <code>WHERE</code> :</p><pre><code class="language-sql">SELECT *
FROM movies
INNER JOIN directors
  ON directors.id = movies.director_id
WHERE directors.name &lt;&gt; 'John Smith';</code></pre><p>Existem algumas diferenças sutis acontecendo por trás dos panos aqui, mas, para o propósito deste artigo, o resultado é o mesmo.</p><p>(Se você não é familiarizado com todas as outras formas de filtrar uma busca em SQL, dê uma olhada <a href="https://www.freecodecamp.org/news/sql-operators-tutorial/">neste artigo comentado anteriormente</a> - em inglês.)</p><h2 id="a-realidade-sobre-escrever-queries-e-joins"><strong>A realidade sobre escrever <em>queries</em> e <em>joins</em></strong></h2><p>Na realidade, eu apenas uso <em>joins</em> de três formas diferentes:</p><h4 id="inner-join-1"><strong><strong><code>INNER JOIN</code></strong></strong></h4><p>O primeiro caso de uso é o de registros onde o relacionamento entre duas tabelas <strong>existe</strong>. Isso é coberto totalmente pelo <code>INNER JOIN</code>.</p><p>Essas são situações como encontrar "<em>filmes que tenham diretores</em>" ou "<em>usuários que tenham postagens</em>".</p><h4 id="left-join"><strong><strong><code>LEFT JOIN</code></strong></strong></h4><p>O segundo caso de uso é o de registros de uma tabela – e se o relacionamento existir– e registros de uma segunda tabela. Esse caso é coberto pelo <code>LEFT JOIN</code>.</p><p>Esse caso é o de situações como "<em>filmes com diretores caso existam</em>" ou "<em>usuários com postagens caso haja alguma</em>"</p><h4 id="exclus-o-do-left-join"><strong>Exclusão do &nbsp;<strong><code>LEFT JOIN</code></strong></strong></h4><p>O terceiro caso de uso mais comum é o nosso segundo caso com uma <code>LEFT JOIN</code>: encontrar os registros de uma tabela que <strong>não </strong>tenham relação com a segunda tabela.</p><p>São situações como "filmes sem diretores" ou "usuários sem postagens".</p><h3 id="dois-tipos-de-joins-muito-teis-"><strong>Dois tipos de <em>joins</em> muito úteis.</strong></h3><p>Eu não acho que já tenha usado uma <code>FULL OUTER JOIN</code> ou uma &nbsp;<code>RIGHT JOIN</code> em uma aplicação em produção. Esses tipos de caso de uso não costumam aparecer frequentemente ou a <em>query</em> pode ser escrita de forma mais simples (no caso do <code>RIGHT JOIN</code>).</p><p>Ocasionalmente, eu já usei o <code>CROSS JOIN</code> para algumas coisas como espalhar registros em um intervalo de datas (como vimos no começo do artigo), mas esse cenário também não costuma aparecer com frequência.</p><p>Então, boas notícias! Existem realmente dois tipos de <em>joins</em> que você precisa saber para 99,9% dos casos que aparecerão: <code>INNER JOIN</code> e <code>LEFT JOIN</code>!</p><p>Se você gostou deste artigo, pode <a href="https://twitter.com/johnmosesman">seguir o autor no Twitter</a>, onde ele fala sobre assuntos relacionados a bancos de dados e a desenvolvimento.</p><p>Obrigado pela leitura!</p><p>Um último conselho para o final: a maioria dos bancos de dados permitirá que você escreva apenas <code>JOIN</code> no lugar do <code>INNER JOIN</code> – isso poupará um pouco de digitação. :)</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Introdução ao Mongoose para MongoDB ]]>
                </title>
                <description>
                    <![CDATA[ Escrito por: Nick Karnik Mongoose é um biblioteca de Modelagem de Dados de Objeto (ou ODM, do inglês:  Object Data Modeling) para MongoDB e Node.js. Ele gerencia o relacionamento entre dados, fornece a validação de esquemas e é usado como tradutor entre objetos no código e a representação desses ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/introducao-ao-mongoose-para-mongodb/</link>
                <guid isPermaLink="false">62a0fd1f08b5de06cf9f6213</guid>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Glaucia Esppenchutz ]]>
                </dc:creator>
                <pubDate>Tue, 28 Jun 2022 16:56:36 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2022/06/1_uTZXsVta4TwghNobMkZeZg.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/introduction-to-mongoose-for-mongodb-d2a7aa593c57/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Introduction to Mongoose for MongoDB</a>
      </p><p>Escrito por: Nick Karnik</p><p>Mongoose é um biblioteca de<em> Modelagem de Dados de Objeto </em>(ou ODM, do inglês: <em>Object Data Modeling</em>) para MongoDB e Node.js. Ele gerencia o relacionamento entre dados, fornece a validação de esquemas e é usado como tradutor entre objetos no código e a representação desses objetos no MongoDB.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/06/0_b5piDNW1dqlkJWKe_.png" class="kg-image" alt="0_b5piDNW1dqlkJWKe_" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/06/0_b5piDNW1dqlkJWKe_.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/06/0_b5piDNW1dqlkJWKe_.png 800w" sizes="(min-width: 720px) 720px" width="800" height="390" loading="lazy"><figcaption>Mapeamento de objetos entre o Node e o MongoDB, gerenciado por meio do Mongoose</figcaption></figure><p>O MongoDB é um banco de dados de documentos NoSQL que não possui esquemas. Isso significa que ele guarda documentos JSON e que a estrutura deles pode variar, já que não há a estrutura rígida igual a dos bancos SQL. Essa é uma das vantagens de se usar NoSQL, pois acelera o desenvolvimento de aplicações e reduz a complexidade das implementações.</p><p>Abaixo vemos um exemplo de como os dados são armazenados dentro do MongoDB x bancos de dados SQL:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/06/0_rcotALFe2LeebN_y_.png" class="kg-image" alt="0_rcotALFe2LeebN_y_" width="594" height="956" loading="lazy"></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/06/0_QOKLctlRwxs5uKVo_.png" class="kg-image" alt="0_QOKLctlRwxs5uKVo_" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/06/0_QOKLctlRwxs5uKVo_.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/06/0_QOKLctlRwxs5uKVo_.png 682w" width="682" height="931" loading="lazy"><figcaption>Documentos do NoSQL x tabelas relacionais do SQL</figcaption></figure><h3 id="terminologia"><strong>Terminologia</strong></h3><h4 id="cole-es-collections-"><strong>Coleções ("<strong>Collections</strong>")</strong></h4><p>As coleções (ou 'Collections', em inglês) no MongoDB são equivalentes às tabelas dos bancos de dados relacionais, podendo guardar múltiplos documentos JSON.</p><h4 id="documentos-documents-"><strong>Documentos ("<strong>Documents</strong>")</strong></h4><p>Os documentos (ou 'Documents', em inglês) equivalem aos registros ou às linhas de dados no SQL. Enquanto uma linha em um banco SQL pode referenciar dados em outras tabelas, os documentos do MongoDB normalmente combinam isso dentro de um único documento.</p><h4 id="campos-fields-"><strong>Campos ("<strong>Fields</strong>")</strong></h4><p>Os campos (ou 'Fields', em inglês) ou atributos são similares a colunas em uma tabela SQL.</p><h4 id="esquema-schema-"><strong>Esquema ("<strong>Schema</strong>")</strong></h4><p>Embora o MongoDB não possua esquemas, o SQL define esquemas por meio da definição de uma tabela. Um "esquema" no Mongoose é uma estrutura de dados de documento (ou a forma de um documento), que é aplicada por meio da camada da aplicação.</p><h4 id="modelos-models-"><strong>Modelos ("<strong>Models</strong>")</strong></h4><p>Os modelos (ou 'Models', em inglês) são construtores de ordem superior, que utilizam um esquema e instanciam um documento, equivalente aos registros de um banco de dados relacional.</p><h3 id="introdu-o"><strong>Introdução</strong></h3><h4 id="instala-o-do-mongodb"><strong>Instalação do MongoDB</strong></h4><p>Antes de começarmos, vamos configurar o Mongo. Você pode escolher <strong>uma das seguintes opções</strong> (nesse artigo usaremos a nº 1):</p><ol><li>Faça o download da versão correta do MongoDB para o seu sistema operacional a partir do <a href="https://www.mongodb.com/download-center#community">site do MongoDB</a> e siga as instruções de instalação</li><li>Crie uma <a href="http://docs.mlab.com/">inscrição gratuita para disponibilizar uma sandbox de seu banco de dados</a></li><li><a href="https://docs.docker.com/samples/library/mongo/">Instale o MongoDB usando o Docker</a>, se preferir.</li></ol><p>Vamos agora navegar por alguns conceitos básicos do Mongoose, implementando um modelo que represente os dados de um catálogo de endereços simplificado.</p><p>Neste exemplo, estou usando o Visual Studio Code, o Node 8.9 e o NPM 5.6. Use seu editor de código favorito, crie um projeto em branco e vamos começar! Usaremos uma sintaxe um pouco mais limitada do ES6 no Node e, por isso, não vamos configurar o Babel.</p><h4 id="instala-o-do-npm"><strong>Instalação do <strong>NPM</strong></strong></h4><p>Vamos até a pasta onde está nosso projeto e inicializá-lo.</p><pre><code class="language-bash">npm init -y</code></pre><p>Agora, vamos instalar o Mongoose e a biblioteca de validação com os seguintes comandos:</p><pre><code class="language-bash">npm install mongoose validator</code></pre><p>O comando acima fará a instalação da versão mais recente dessas bibliotecas. A sintaxe do Mongoose nesse artigo é específica para a versão 5 do Mongoose ou superior.</p><h4 id="conex-o-com-o-banco-de-dados"><strong>Conexão com o banco de dados</strong></h4><p>Crie um arquivo <code>./src/database.js</code> na raiz do seu projeto.</p><p>Agora, vamos adicionar uma classe e um método simples que faça a conexão com o banco de dados.</p><p>O endereço de conexão vai variar de acordo com a instalação que você fez.</p><pre><code class="language-js">let mongoose = require('mongoose');

const server = '127.0.0.1:27017'; // COLOQUE O NOME DO SEU SERVIDOR DO BANCO DE DADOS
const database = 'fcc-Mail';      // COLOQUE O NOME DO SEU BANCO DE DADOS

class Database {
  constructor() {
    this._connect()
  }
  
_connect() {
     mongoose.connect(`mongodb://${server}/${database}`)
       .then(() =&gt; {
         console.log('Database connection successful')
       })
       .catch(err =&gt; {
         console.error('Database connection error')
       })
  }
}

module.exports = new Database()</code></pre><p>A chamada <code>require('mongoose')</code> retorna um objeto do tipo "Singleton". Isso significa que a primeira vez que o chamar, ele criará uma instância da classe do Mongoose e o retornará. Em chamadas subsequentes, ele retornará a mesma instância que foi criada da primeira vez. Isso acontece devido ao modo como o modulo de importação/exportação funciona no ES6.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/06/0_RvVsD_byUakUzuCj_.png" class="kg-image" alt="0_RvVsD_byUakUzuCj_" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/06/0_RvVsD_byUakUzuCj_.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/06/0_RvVsD_byUakUzuCj_.png 800w" sizes="(min-width: 720px) 720px" width="800" height="339" loading="lazy"><figcaption>Fluxo de trabalho do módulo import/require</figcaption></figure><p>De modo similar, transformamos nossa classe "Database" em um <em>singleton </em>retornando uma instância da classe no formato de uma declaração <code>module.exports</code>, pois só precisaremos de uma única conexão com nosso banco de dados.</p><p>O ES6 faz a criação de padrões <em>singleton</em> (ou de instância única) de modo bem fácil devido a maneira como o carregador do módulo (em inglês, <em>module loader</em>) funciona, criando o <em>cache</em> das respostas de arquivos importados anteriormente.</p><h3 id="esquema-x-modelos-no-mongoose"><strong>Esquema x Modelos no Mongoose</strong></h3><p>Um modelo Mongoose é um "wrapper" ou "empacotador" de esquemas Mongoose. Um esquema Mongoose define a estrutura do documento, os valores padrões, validadores, entre outros. O modelo Mongoose, por sua vez, fornece uma interface com o banco de dados para a criação, busca, atualização, deleção de registros, e assim por diante.</p><p>Criar um modelo Mongoose consiste primariamente em três partes:</p><h4 id="1-fazer-a-refer-ncia-ao-mongoose"><strong><strong><strong><strong>1. </strong></strong></strong>Fazer a referência ao Mongoose</strong></h4><pre><code class="language-js">let mongoose = require('mongoose')</code></pre><p>Essa referência será a mesma que foi retornada quando conectamos com o banco de dados, o que significa que as definições de esquema e modelo não precisarão ser explícitas novamente para se conectar ao banco.</p><h4 id="2-definir-o-esquema"><strong><strong>2. </strong>Definir o esquema</strong></h4><p>O esquema define as propriedades do documento através de um objeto, onde o nome da chave corresponde ao nome da propriedade na coleção.</p><pre><code class="language-js">let emailSchema = new mongoose.Schema({
  email: String
})</code></pre><p>Aqui, definimos a propriedade chamada <strong>email </strong>com o esquema no tipo <strong>String, </strong>que, internamente, mapeia um validador interno, que será disparado quando o modelo for salvo. Caso o valor do dado salvo não seja no formato <strong>string</strong>, ele falhará.</p><p>Os seguintes tipos de esquemas são permitidos: </p><ul><li>Array </li><li>Boolean (ou booleano, em português)</li><li>Buffer</li><li>Date (ou formato de data, em português)</li><li>Mixed (um tipo genérico/flexível de dados)</li><li>Number (ou numérico, em português)</li><li>ObjectId</li><li>String</li></ul><p>Os tipos "Mixed" e "ObjectId" são definidos em <code>require(‘mongoose’).Schema.Types</code>.</p><h4 id="3-exportar-um-modelo"><strong><strong><strong><strong>3. </strong></strong></strong>Exportar um modelo</strong></h4><p>Precisamos chamar o construtor de modelos na instância do Mongoose e passá-lo para a sua coleção, assim como sua referência na definição do esquema.</p><pre><code class="language-js">module.exports = mongoose.model('Email', emailSchema)</code></pre><p>Vamos combinar o código acima no arquivo <code>./src/models/email.js</code> para definir o conteúdo de um modelo básico de e-mail:</p><pre><code class="language-js">let mongoose = require('mongoose')

let emailSchema = new mongoose.Schema({
  email: String
})

module.exports = mongoose.model('Email', emailSchema)</code></pre><p>A definição de um esquema deve ser simples, mas sua complexidade normalmente é baseada na aplicação dos requerimentos. Esquemas podem ser reutilizados e podem conter diversos esquemas derivados (no inglês, "child-schemas"). No exemplo acima, o valor da propriedade "email" é de um tipo simples. No entanto, poderia ser do tipo objeto com propriedades adicionais acopladas.</p><p>Podemos criar uma instância de um modelo que criamos acima e populá-la usando a seguinte sintaxe:</p><pre><code class="language-js">let EmailModel = require('./email')

let msg = new EmailModel({
  email: 'ada.lovelace@gmail.com'
})</code></pre><p>Vamos aprimorar o esquema "email" para fazer com que o valor de <em>email</em> inserido seja de propriedade única, seja um campo obrigatório e tenha seus valores convertidos para caracteres minúsculos antes de ser salvo. Podemos também adicionar uma função de validação que garanta que o endereço de e-mail seja válido. Vamos referenciar e usar a biblioteca validadora que instalamos anteriormente.</p><pre><code class="language-js">let mongoose = require('mongoose')
let validator = require('validator')

let emailSchema = new mongoose.Schema({
  email: {
    type: String,
    required: true,
    unique: true,
    lowercase: true,
    validate: (value) =&gt; {
      return validator.isEmail(value)
    }
  }
})

module.exports = mongoose.model('Email', emailSchema)</code></pre><h3 id="opera-es-b-sicas"><strong>Operações básicas</strong></h3><p>O Mongoose tem uma API flexível e fornece várias formas de concluir uma tarefa. Não vamos focar nas variações, pois isso está fora do escopo deste artigo, mas lembre-se de que a maior parte das operações pode ser feita de mais de uma maneira sintaticamente ou por meio da arquitetura da aplicação.</p><h4 id="criando-registros"><strong>Criando registros</strong></h4><p>Vamos criar uma instância de um modelo de e-mail e salvá-la no banco:</p><pre><code>let EmailModel = require('./email')

let msg = new EmailModel({
  email: 'ADA.LOVELACE@GMAIL.COM'
})

msg.save()
   .then(doc =&gt; {
     console.log(doc)
   })
   .catch(err =&gt; {
     console.error(err)
   })</code></pre><p>O resultado é o retorno de um documento apontando o sucesso da transação:</p><pre><code class="language-json">{ 
  _id: 5a78fe3e2f44ba8f85a2409a,
  email: 'ada.lovelace@gmail.com',
  __v: 0 
}</code></pre><p>Os seguintes campos são retornados (campos internos são em forma de prefixo e seguidos de uma linha sublinhada (ou "underscore", no inglês)):</p><ol><li>O campo <code>_id</code> é gerado automaticamente pelo Mongo e é a chave primária da sua coleção. Seu valor é único de forma a facilitar a identificação do documento.</li><li>O valor <code>email</code> é retornado. Perceba que ele está em minúsculas pois especificamos o <code>lowercase:true</code> nos atributos do esquema.</li></ol><p>Se tentar repetir a mesma operação que fizemos acima, você receberá uma mensagem de erro, pois também especificamos que este campo deverá ser único.</p><h4 id="buscando-o-registro"><strong>Buscando o registro</strong></h4><p>Vamos tentar recuperar um registro que salvamos anteriormente em nosso banco de dados. A classe do nosso modelo expõe vários métodos estáticos e de instância para realizar operações no banco de dados. Vamos agora tentar encontrar o registro que criamos anteriormente usando o método de busca e passar o e-mail como parâmetro.</p><pre><code>EmailModel
  .find({
    email: 'ada.lovelace@gmail.com'   // nossa busca
  })
  .then(doc =&gt; {
    console.log(doc)
  })
  .catch(err =&gt; {
    console.error(err)
  })</code></pre><p>O documento retornado será similar ao que vimos quando criamos o registro em si:</p><pre><code class="language-json">{ 
  _id: 5a78fe3e2f44ba8f85a2409a,
  email: 'ada.lovelace@gmail.com',
  __v: 0 
}</code></pre><h4 id="atualizando-o-registro"><strong>Atualizando o registro</strong></h4><p>Agora, vamos modificar o registro acima mudando o endereço de e-mail e adicionando outro campo, tudo em uma única operação. Por questões de performance, o Mongoose não vai retornar o documento atualizado e, então, precisaremos passar um parâmetro adicional pedindo-o:</p><pre><code class="language-js">EmailModel
  .findOneAndUpdate(
    {
      email: 'ada.lovelace@gmail.com'  // nossa busca
    }, 
    {
      email: 'theoutlander@live.com'   // campo a ser atualizado
    },
    {
      new: true,                       // retorne o doc atualizado
      runValidators: true              // valide antes de atualizar
    })
  .then(doc =&gt; {
    console.log(doc)
  })
  .catch(err =&gt; {
    console.error(err)
  })</code></pre><p>O documento retornado terá o campo <em>email</em> atualizado: </p><pre><code class="language-json">{ 
  _id: 5a78fe3e2f44ba8f85a2409a,
  email: 'theoutlander@live.com',
  __v: 0 
}</code></pre><h4 id="apagando-o-registro"><strong>Apagando o registro</strong></h4><p>Para essa operação, usaremos a chamada <code>findOneAndRemove</code> para apagar o registro. O retorno é o documento original que foi removido:</p><pre><code class="language-js">EmailModel
  .findOneAndRemove({
    email: 'theoutlander@live.com'
  })
  .then(response =&gt; {
    console.log(response)
  })
  .catch(err =&gt; {
    console.error(err)
  })</code></pre><h3 id="auxiliares-ou-helpers-no-ingl-s-">Auxiliares (ou "Helpers", no inglês)</h3><p>Vimos algumas das funcionalidades básicas conhecidas, como operações CRUD (Create, Read, Update, Delete), mas o Mongoose também fornece a possibilidade de configurar diversos outros tipos de métodos e propriedades auxiliares. Esses podem ser usados para simplificar futuramente o trabalho com os dados. </p><p>Vamos criar um esquema para um usuário no <code>./src/models/user.js</code> com os campos <code>firstName</code> e <code>lastName</code>:</p><pre><code class="language-js">let mongoose = require('mongoose')

let userSchema = new mongoose.Schema({
  firstName: String,
  lastName: String
})

module.exports = mongoose.model('User', userSchema)</code></pre><h4 id="propriedade-virtual-virtual-property-"><strong>Propriedade virtual (Virtual Property)</strong></h4><p>Uma propriedade virtual não é persistida no banco de dados. Podemos adicioná-la ao nosso esquema como uma propriedade auxiliar para obter e definir valores.</p><p>Vamos criar uma propriedade virtual chamada <code>fullName</code>, que pode ser usada para definir os valores de <code>firstName</code> e <code>lastName</code>, além de recuperá-los como um valor combinado quando lidos:</p><pre><code class="language-js">userSchema.virtual('fullName').get(function() {
  return this.firstName + ' ' + this.lastName
})

userSchema.virtual('fullName').set(function(name) {
  let str = name.split(' ')
  
  this.firstName = str[0]
  this.lastName = str[1]
})</code></pre><p>Callbacks ou "retornos de chamada" para obter e definir valores devem usar as palavras-chave da função, pois precisamos acessar o modelo através da palavra-chave <code>this</code>. Usar "fat arrow functions" (ou funções de setas grossas, em português) mudará a referência de <code>this</code>.</p><p>Agora, podemos definir <code>firstName</code> &nbsp;e <code>lastName</code> através da atribuição de <code>fullName</code>:</p><pre><code class="language-js">let model = new UserModel()

model.fullName = 'Thomas Anderson'

console.log(model.toJSON())  // Mostra os campos do modelo como JSON
console.log()
console.log(model.fullName)  // Mostra o nome completo</code></pre><p>O resultado da execução do código acima é esse:</p><pre><code class="language-bash">{ _id: 5a7a4248550ebb9fafd898cf,
  firstName: 'Thomas',
  lastName: 'Anderson' }
  
Thomas Anderson</code></pre><h4 id="m-todos-instanciados"><strong>Métodos instanciados</strong></h4><p>Podemos criar métodos auxiliares personalizados no nosso esquema e acessá-los através de uma instancia de método. Esses métodos terão acessos ao objeto do modelo e podem ser usados de modo bastante criativo. Por exemplo, poderíamos criar um métodos para encontrar todas as pessoas que tenham o mesmo nome da instância atual.</p><p>Nesse exemplo, vamos criar uma função que retorne as iniciais do usuário em questão. Vamos adicionar o método auxiliar personalizado chamado <code>getInitials</code> &nbsp;ao esquema:</p><pre><code class="language-js">userSchema.methods.getInitials = function() {
  return this.firstName[0] + this.lastName[0]
}</code></pre><p>Esse método estará acessível através da instância do modelo:</p><pre><code class="language-js">let model = new UserModel({
  firstName: 'Thomas',
  lastName: 'Anderson'
})

let initials = model.getInitials()

console.log(initials) // O resultado será: TA</code></pre><h4 id="m-todos-est-ticos"><strong>Métodos estáticos</strong></h4><p>Similar aos métodos instanciados, podemos criar também métodos estáticos no nosso esquema. Vamos criar um método para trazer todos os usuários que temos em nosso banco de dados:</p><pre><code class="language-js">userSchema.statics.getUsers = function() {
  return new Promise((resolve, reject) =&gt; {
    this.find((err, docs) =&gt; {
      if(err) {
        console.error(err)
        return reject(err)
      }
      
      resolve(docs)
    })
  })
}</code></pre><p>Quando chamamos <code>getUsers</code> da classe Model, ele retornará todos os usuários de nosso banco de dados:</p><pre><code class="language-js">UserModel.getUsers()
  .then(docs =&gt; {
    console.log(docs)
  })
  .catch(err =&gt; {
    console.error(err)
  })</code></pre><p>Adicionar um método instanciado e um estático é uma ótima abordagem para implementar uma interface de interação de banco de dados em coleções e registros. </p><h4 id="middleware"><strong><strong><strong><strong>Middleware</strong></strong></strong></strong></h4><p>Middleware são funções executadas em estágios específicos de um "pipeline" (ou caminho, no português). O Mongoose tem suporte para middleware nas seguintes operações:</p><ul><li>Agregação</li><li>Documento</li><li>Modelo</li><li>Busca (ou <em>query</em>, em inglês)</li></ul><p>Por exemplo, o modelo tem funções <code>pre</code> e <code>post</code>, que aceitam dois parâmetros:</p><ol><li>Tipo do evento ("init", "validate", "save", "remove")</li><li>Uma "callback", que é executada com o <code>this</code> referenciando a instância do modelo</li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/06/0_iZwmyy25FSxuxXlH_.png" class="kg-image" alt="0_iZwmyy25FSxuxXlH_" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/06/0_iZwmyy25FSxuxXlH_.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/06/0_iZwmyy25FSxuxXlH_.png 800w" sizes="(min-width: 720px) 720px" width="800" height="310" loading="lazy"><figcaption>Exemplo de middleware(também conhecido como chamadas pre e post)</figcaption></figure><p>Vamos executar um exemplo de adição de dois campos, chamados <code>createdAt</code> e <code>updatedAt</code>, no nosso esquema:</p><pre><code class="language-js">let mongoose = require('mongoose')

let userSchema = new mongoose.Schema({
  firstName: String,
  lastName: String,
  createdAt: Date,
  updatedAt: Date
})

module.exports = mongoose.model('User', userSchema)</code></pre><p>Quando <code>model.save()</code> é chamado, é executado um evento de <code>pre(‘save’, …)</code> e <code>post(‘save’, …)</code>. Para o segundo parâmetro, você pode passar a função que é chamada quando o evento é disparado. Essas funções recebem um parâmetro para a próxima função na cadeia de middleware.</p><p>Vamos adicionar um "hook" (ou gancho, no português) de "pre-save" (algo como "salvamento prévio", na tradução) &nbsp;para definir os valores de <code>createdAt</code> e <code>updatedAt</code>:</p><pre><code class="language-js">userSchema.pre('save', function (next) {
  let now = Date.now()
   
  this.updatedAt = now
  // Define o valor para createdAt apenas se ele for nulo
  if (!this.createdAt) {
    this.createdAt = now
  }
  
  // Chama a próxima função na cadeia de pre-save
  next()    
})</code></pre><p>Agora, vamos criar e salvar nosso modelo:</p><pre><code class="language-js">let UserModel = require('./user')

let model = new UserModel({
  fullName: 'Thomas Anderson'
}

msg.save()
   .then(doc =&gt; {
     console.log(doc)
   })
   .catch(err =&gt; {
     console.error(err)
   })</code></pre><p>Você deverá ver os valores para <code>createdAt</code> &nbsp;e <code>updatedAt</code> quando o registro for criado e exibido:</p><pre><code class="language-json">{ _id: 5a7bbbeebc3b49cb919da675,
  firstName: 'Thomas',
  lastName: 'Anderson',
  updatedAt: 2018-02-08T02:54:38.888Z,
  createdAt: 2018-02-08T02:54:38.888Z,
  __v: 0 }</code></pre><h4 id="plug-ins"><strong><strong>Plug</strong>-<strong>ins</strong></strong></h4><p>Suponha que você queira acompanhar quando um registro foi criado ou quando foi atualizado pela última vez em cada coleção do nosso banco de dados. Ao invés de repetir o processo acima, você pode criar um plug-in e aplicar em cada esquema:</p><p>Vamos criar um arquivo em <code>./src/model/plugins/timestamp.js</code> e replicar a funcionalidade acima de modo a se tornar um módulo reutilizável:</p><pre><code class="language-js">module.exports = function timestamp(schema) {

  // Adiciona os dois campos ao esquema
  schema.add({ 
    createdAt: Date,
    updatedAt: Date
  })

  //Cria o pre-save hook
  schema.pre('save', function (next) {
    let now = Date.now()
   
    this.updatedAt = now
    // Define o valor para createdAt apenas se ele for nulo
    if (!this.createdAt) {
      this.createdAt = now
    }
   // Chama a próxima função na cadeia de pre-save
   next()    
  })
}</code></pre><p>Para usar esse plug-in, simplesmente o passamos para nossos esquemas que deveriam utilizar essa funcionalidade:</p><pre><code class="language-js">let timestampPlugin = require('./plugins/timestamp')

emailSchema.plugin(timestampPlugin)
userSchema.plugin(timestampPlugin)</code></pre><h3 id="criando-pesquisas"><strong>Criando pesquisas</strong></h3><p>O Mongoose tem uma API muito rica, que lida com diversos tipos de operações complexas que provém do MongoDB. Considere uma busca onde podemos criar incrementalmente os componentes de pesquisa.</p><p>Nesse exemplo, nós vamos: </p><ol><li>Encontrar todos os usuários</li><li>Pular os primeiros 100 registros</li><li>Limitar o resultado a 10 registros</li><li>Ordenar os resultados pelo campo firstName </li><li>Selecionar firstName </li><li>Executar a busca</li></ol><pre><code class="language-js">UserModel.find()                   // encontra todos os usuários
         .skip(100)                // pula os primeiros 100 registos
         .limit(10)                // limita a 10 itens
         .sort({firstName: 1}      // ordena firstName de forma ascendente
         .select({firstName: true} // seleciona firstName apenas
         .exec()                   // executa a busca
         .then(docs =&gt; {
            console.log(docs)
          })
         .catch(err =&gt; {
            console.error(err)
          })</code></pre><h3 id="finalizando"><strong>Finalizando</strong></h3><p>Nós mal exploramos a superfície das funcionalidades do Mongoose. Esta é uma biblioteca rica, cheia de recursos poderosos e que trazem diversão ao trabalhar com modelos de dados na camada de aplicação.</p><p>Embora você possa interagir com o MongoDB usando o Mongo Driver, o Mongoose vai simplificar essa interação, permitindo que você modele relacionamentos entre os dados e os valide de modo fácil.</p><p>Curiosidade: &nbsp;o <a href="http://mongoosejs.com/" rel="noopener"><strong><strong>Mongoose</strong></strong></a><strong> </strong>foi criado por<strong> <strong>Valeri Karpov</strong>, </strong>que é um engenheiro incrível!<strong> </strong>Ele cunhou o termo<strong> </strong><a href="http://thecodebarbarian.com/2013/04/29//easy-web-prototyping-with-mongodb-and-nodejs" rel="noopener"><strong><strong>The MEAN Stack</strong></strong></a><strong> </strong>(texto em inglês).</p><h4 id="se-esse-artigo-foi-til-siga-o-autor-no-twitter-"><strong>Se esse artigo foi útil, &nbsp;<a href="https://twitter.com/intent/follow?screen_name=theoutlander">siga o autor no Twitter</a>!</strong></h4><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/06/1_278_8HmTEdaRAqFYUemQvQ.png" class="kg-image" alt="1_278_8HmTEdaRAqFYUemQvQ" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/06/1_278_8HmTEdaRAqFYUemQvQ.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/06/1_278_8HmTEdaRAqFYUemQvQ.png 800w" sizes="(min-width: 720px) 720px" width="800" height="392" loading="lazy"><figcaption><a href="https://twitter.com/intent/follow?screen_name=theoutlander" rel="noopener">Você também pode gostar do workshop do autor no YouTube: </a><a href="https://youtu.be/egeHq-lYyxo" rel="noopener">How to Build a REST API with Node | Express | Mongo</a> (em inglês)</figcaption></figure> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
