• 10 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 20/12/2021

Implémentez une lecture de données (READ) complexe

À cette étape du cours, nous sommes capables de récupérer les données à afficher dans notre plateforme de blogging, grâce aux opérations vues au chapitre précédent.

Réfléchissons maintenant à la fonctionnalité suivante : nous voulons afficher sur la page d’accueil le nom des derniers articles, du plus récent au plus ancien. Comme nous l’avons vu, c'est possible, grâce à une requête dérivée comme findByOrderByDateDesc().

Oui mais voilà, en tant que développeur consciencieux, nous nous posons toujours la question de la performance. La méthode précédente retournerait tout le document alors que, dans notre situation, nous avons uniquement besoin de 2 informations : son id et son nom.

C’est là qu’entrent en jeu les projections ! Une projection correspond à la récupération d’une sous-partie du document. Voyons ensemble comment faire !

Implémentez une projection

Je vais vous présenter 2 méthodes. L'une est très simple, et l’autre demande de mieux connaître MongoDB. Commençons avec une projection basée sur une interface. Je vous montre comment faire dans la vidéo ci-dessous :

Comme nous en avons l’habitude après une démonstration, reprenons le code créé et commençons par l’interface LigthPost :

package com.openclassrooms.blogdatalayer.model;
public interface LightPost {
     public String getId();
     public String getName();
}

J’ai créé cette interface dans le package model. Cet article léger, comme on le comprend par le nom LightPost, est composé uniquement d’un accesseur (getter) pour l’id et d’un autre pour le name

Maintenant, reprenons notre interface PostRepository :

package com.openclassrooms.blogdatalayer.repository;

import java.util.List;

import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;

import com.openclassrooms.blogdatalayer.model.LightPost;
import com.openclassrooms.blogdatalayer.model.Post;

@Repository
public interface PostRepository extends MongoRepository<Post, String> {

     public List<LightPost> findByOrderByDateDesc();

}

La méthode findByOrderByDateDesc() a comme particularité au niveau du type retour de spécifier l’interface de notre projection, à savoir LightPost.

Et c’est tout ! Le tour est joué, Spring Data MongoDB se contente de récupérer l’id et le name, et non toutes les informations du document.

Vous implémentez la méthode run de la même manière que précédemment :

     @Override
     public void run(String... args) throws Exception {

          List<LightPost> allPosts = postRepository.findByOrderByDateDesc();
          allPosts.stream().forEach((post) -> logger.info(post.getName()));

     }

Maintenant, intéressons-nous à la deuxième façon de faire. Comme je vous le disais, elle nécessite de mieux connaître MongoDB car elle implique de définir la requête telle qu’on le ferait directement sur MongoDB. On appelle cela les requêtes natives, ou Native Queries, en anglais.

L’avantage est que vous n’avez pas besoin de créer une nouvelle interface. Tout va se passer dans PostRepository grâce à l’annotation @Query

package com.openclassrooms.blogdatalayer.repository;

import java.util.List;

import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.stereotype.Repository;

import com.openclassrooms.blogdatalayer.model.LightPost;
import com.openclassrooms.blogdatalayer.model.Post;

@Repository
public interface PostRepository extends MongoRepository<Post, String> {

     List<LightPost> findByOrderByDateDesc();

     @Query(value="{}", fields="{_id : 1, name : 1}", sort = "{ date : -1 }")
     List<Post> findIdAndNameExcludeOthers();

}

J’ai défini un nouveau prototype, cette fois-ci le nom est arbitraire. J’ai choisi un nom très explicite, à savoir findIdAndNameExcludeOthers()

Notez que la méthode est annotée @Query. Cette annotation, fournie par Spring Data MongoDB, permet d’indiquer une requête native :

  • value=”{}” : signifie qu’il n’y a pas de filtres et que tous les documents seront récupérés ;

  • fields=”{_id : 1; name :1}” : signifie qu’on veut récupérer les champs _id et name (donc pour récupérer un autre champ il suffit d’ajouter [nom du champ] : 1 ;

  • sort = “{date : -1}” : signifie qu’on trie par le champ date et l’indicatif -1 en mode décroissant (à l’inverse, l’indicatif 1 indiquerait un tri croissant).

Il est important de retenir que Spring Data MongoDB a cet énorme avantage d’offrir :

  • une surcouche applicative qui vous dispense d’écrire des requêtes natives (ce qui rend les choses plus faciles, admettons-le)  ;

  • la capacité d’exécuter des requêtes natives (pour les plus férus de MongoDB). 

Finalement, qui peut le plus peut le moins !

Un dernier détail, dans cette section nous avons parlé de tri. Si cela vous est un peu confus, je vous encourage à consulter cette section de la documentation officielle.

À vous de jouer

Implémentez une projection pour les tutoriels, récupérez uniquement le nom et la description courte. Faites-le :

  1. Avec une projection basée sur une interface.

  2. Avec une projection définie via l’annotation @Query.

N’hésitez pas à vérifier votre travail en consultant le repository GitHub du cours à la branche p2c3. Il contient le code correspondant.

Implémentez une agrégation

MongoDB offre la capacité d’exécuter des agrégations, c’est-à-dire d’exécuter une chaîne d’opérations effectuée sur les documents.

Cette fonctionnalité permet de traiter vos données de manière efficace et performante, directement au niveau de la base de données. Cela en opposition à une récupération brute de vos données et à un traitement via des algorithmes dans votre code.

Implémentez une agrégation simple

Découvrons dans la vidéo ci-dessous comment Spring Data MongoDB nous permet de mettre en œuvre une agrégation simple :

Une fois n’est pas coutume, parcourons le code vu pendant cette démonstration :

package com.openclassrooms.blogdatalayer.repository;

import java.util.List;

import org.springframework.data.mongodb.repository.Aggregation;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;

import com.openclassrooms.blogdatalayer.model.Post;
import com.openclassrooms.blogdatalayer.model.PostAggregate;

@Repository
public interface PostRepository extends MongoRepository<Post, String> {

     @Aggregation("{ '$project': { '_id' : '$name' } }")
     public List<String> findAllName();

}

L’information principale est l’annotation @Aggregation. Cette annotation permet de définir notre séquence d’opérations (en anglais vous entendrez parler de pipeline), comme on le ferait dans une requête native.

Dans la démonstration, nous avons implémenté l’agrégation suivante : { '$project': { '_id' : '$name' } }.
Cette agrégation ressemble à une projection. La séquence contient une unique opération qui correspond à l’extraction de toutes les valeurs de champ name. Ces valeurs sont ajoutées à une liste.

Le code de la méthode run() ci-dessous nous a permis de vérifier l’implémentation :

@Override
    public void run(String... args) throws Exception {
          List<String> names = postRepository.findAllName();
          names.forEach(logger::info);
    }

Voyons maintenant un cas plus complexe, j’ai choisi de traiter la problématique suivante : comment regrouper les articles par date ?

Implémentez une agrégation complexe

Découvrons dans la vidéo ci-dessous comment mettre en œuvre cette agrégation complexe :

Parcourons le code vu pendant cette démonstration :

package com.openclassrooms.blogdatalayer.repository;

import java.util.List;

import org.springframework.data.mongodb.repository.Aggregation;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;

import com.openclassrooms.blogdatalayer.model.Post;
import com.openclassrooms.blogdatalayer.model.PostAggregate;

@Repository
public interface PostRepository extends MongoRepository<Post, String> {

     @Aggregation("{ $group: { _id : $date, names : { $addToSet : $name } } }")
     public List<PostAggregate> groupByDate();

}

Avec l’agrégation { $group: { _id : $date, names : { $addToSet : $name } } }, j’ai regroupé les articles par date.
L’instruction $group va regrouper vos documents par la valeur du champ $date. 

Ensuite pour chaque date, j’extrais du groupe de documents le champ $name de chaque document. Je crée ainsi une liste des noms des articles grâce à l’instruction $addToSet. 

À noter que dans le cas de cette agrégation plus complexe, le type retour est différent : List<PostAggregate>. En effet, nous avons dû créer une nouvelle classe qui servira de conteneur pour le résultat de l’agrégation. Voici son contenu :

package com.openclassrooms.blogdatalayer.model;

import java.util.Date;
import java.util.List;

import org.springframework.data.annotation.Id;

public class PostAggregate {

     private @Id Date date;
     private List<String> names;

     public Date getDate() {
          return date,
     }
     public List<String> getNames() {
          return names;
     }
}

Cette classe contient 2 attributs :

  • date, annoté @Id, qui correspond à la valeur utilisée pour regrouper nos documents ;

  • names, qui reçoit la liste des noms des articles pour la date définie dans l’attribut précédent.

Le code de la méthode run() ci-dessous sert à vérifier votre implémentation :

@Override
    public void run(String... args) throws Exception {
         List<PostAggregate> postAggregates =
                postRepository.groupByDate();
         postAggregates .stream().forEach(
                (aggregate) -> logger.info(
                   aggregate.getDate()
                  + “ : “
                   + aggregate.getNames().size())
              );
    }

Finalement, le plus complexe n’est pas tant de manipuler Spring Data MongoDB pour réaliser des agrégations, que de comprendre cette fonctionnalité native de MongoDB.

À vous de jouer

Implémentez une agrégation pour les tutoriels en prenant en compte les besoins fonctionnels suivants :

  • grouper tous les tutoriels par leur catégorie ;

  • récupérer le nom et la description courte.

Je vous propose 2 astuces pour vous aider :

  • créez une nouvelle classe, par exemple TutorialExtract, qui aura un attribut name et un attribut shortDescription ;

  • utilisez cet objet comme type de la liste dans TutorialAggregate.

N’hésitez pas à vérifier votre travail en consultant le repository GitHub du cours à la branche p2c3. Il contient le code correspondant.

En résumé

  • MongoDB offre 2 fonctionnalités très intéressantes pour la lecture de données, les projections et les agrégations.

  • Les projections peuvent être codées grâce à des interfaces (interface based projection) ou grâce à l’annotation @Query.

  • Les agrégations peuvent être codées grâce à l’annotation @Aggregation, et requièrent la création d’une nouvelle classe pour obtenir le résultat de l’agrégation.

Félicitations ! Vous venez de terminer la deuxième partie de ce cours, et je suis très fier du travail que vous avez réalisé ! Maintenant, continuons avec la troisième partie et les opérations Create, Update et Delete.

Bon c’est vrai j’avoue, avant il y a un quiz, mais je suis sûr que vous allez assurer.

Exemple de certificat de réussite
Exemple de certificat de réussite