MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Spring Data MongoDB :projections et agrégations

1. Présentation

Spring Data MongoDB fournit des abstractions simples de haut niveau au langage de requête natif MongoDB. Dans cet article, nous explorerons la prise en charge du framework Projections et Agrégation.

Si vous êtes nouveau sur ce sujet, reportez-vous à notre article d'introduction Introduction à Spring Data MongoDB.

2. Projection

Dans MongoDB, les projections sont un moyen de récupérer uniquement les champs obligatoires d'un document à partir d'une base de données. Cela réduit la quantité de données qui doit être transférée du serveur de base de données au client et augmente donc les performances.

Avec Spring Data MongDB, les projections peuvent être utilisées à la fois avec MongoTemplate et MongoRepository.

Avant d'aller plus loin, regardons le modèle de données que nous allons utiliser :

@Document
public class User {
    @Id
    private String id;
    private String name;
    private Integer age;
    
    // standard getters and setters
}

2.1. Projections à l'aide de MongoTemplate

Le include() et exclure() méthodes sur le Champ class est utilisé pour inclure et exclure des champs respectivement :

Query query = new Query();
query.fields().include("name").exclude("id");
List<User> john = mongoTemplate.find(query, User.class);

Ces méthodes peuvent être chaînées pour inclure ou exclure plusieurs champs. Le champ marqué comme @Id (_id dans la base de données) est toujours récupéré à moins d'être explicitement exclu.

Les champs exclus sont null dans l'instance de classe de modèle lorsque les enregistrements sont extraits avec projection. Dans le cas où les champs sont d'un type primitif ou de leur classe wrapper, alors la valeur des champs exclus sont les valeurs par défaut des types primitifs.

Par exemple, chaîne serait nulle , entier /Entier serait 0 et booléen /Booléen serait faux .

Ainsi dans l'exemple ci-dessus, le nom le champ serait John , identifiant serait null et âge serait 0.

2.2. Projections à l'aide de MongoRepository

Lors de l'utilisation de MongoRepositories, les champs de @Query l'annotation peut être définie au format JSON :

@Query(value="{}", fields="{name : 1, _id : 0}")
List<User> findNameAndExcludeId();

Le résultat serait le même que l'utilisation de MongoTemplate. La value="{}" indique aucun filtre et donc tous les documents seront récupérés.

3. Agrégation

L'agrégation dans MongoDB a été conçue pour traiter les données et renvoyer les résultats calculés. Les données sont traitées par étapes et la sortie d'une étape est fournie comme entrée à l'étape suivante. Cette capacité à appliquer des transformations et à effectuer des calculs sur les données par étapes fait de l'agrégation un outil d'analyse très puissant.

Spring Data MongoDB fournit une abstraction pour les requêtes d'agrégation natives en utilisant les trois classes Aggregation qui encapsule une requête d'agrégation, AggregationOperation qui englobe les étapes individuelles du pipeline et AggregationResults qui est le conteneur du résultat produit par agrégation.

Pour effectuer une agrégation, commencez par créer des pipelines d'agrégation à l'aide des méthodes de générateur statique sur Agrégation class, puis créez une instance de Aggregation en utilisant newAggregation() méthode sur l'Agrégation class et enfin exécuter l'agrégation en utilisant MongoTemplate :

MatchOperation matchStage = Aggregation.match(new Criteria("foo").is("bar"));
ProjectionOperation projectStage = Aggregation.project("foo", "bar.baz");
        
Aggregation aggregation 
  = Aggregation.newAggregation(matchStage, projectStage);

AggregationResults<OutType> output 
  = mongoTemplate.aggregate(aggregation, "foobar", OutType.class);

Veuillez noter que MatchOperation et ProjectionOperation implémenter AggregationOperation . Il existe des implémentations similaires pour d'autres pipelines d'agrégation. OutType est le modèle de données pour la sortie attendue.

Nous allons maintenant examiner quelques exemples et leurs explications pour couvrir les principaux pipelines et opérateurs d'agrégation.

L'ensemble de données que nous utiliserons dans cet article répertorie les détails de tous les codes postaux aux États-Unis qui peuvent être téléchargés à partir du référentiel MongoDB.

Examinons un exemple de document après l'avoir importé dans une collection appelée zips dans le test base de données.

{
    "_id" : "01001",
    "city" : "AGAWAM",
    "loc" : [
        -72.622739,
        42.070206
    ],
    "pop" : 15338,
    "state" : "MA"
}

Par souci de simplicité et pour rendre le code concis, dans les prochains extraits de code, nous supposerons que tous les éléments statiques méthodes d'Agrégation classe sont importées statiquement.

3.1. Obtenez tous les États avec une population supérieure à 10 millions Order par population décroissante

Ici, nous aurons trois pipelines :

  1. $group étape résumant la population de tous les codes postaux
  2. $match étape pour filtrer les États de plus de 10 millions d'habitants
  3. $sort étape pour trier tous les documents par ordre décroissant de population

La sortie attendue aura un champ _id comme état et un champ statePop avec la population totale de l'État. Créons un modèle de données pour cela et exécutons l'agrégation :

public class StatePoulation {
 
    @Id
    private String state;
    private Integer statePop;
 
    // standard getters and setters
}

Le @identifiant l'annotation mappera le _id champ de la sortie à état dans le modèle :

GroupOperation groupByStateAndSumPop = group("state")
  .sum("pop").as("statePop");
MatchOperation filterStates = match(new Criteria("statePop").gt(10000000));
SortOperation sortByPopDesc = sort(Sort.by(Direction.DESC, "statePop"));

Aggregation aggregation = newAggregation(
  groupByStateAndSumPop, filterStates, sortByPopDesc);
AggregationResults<StatePopulation> result = mongoTemplate.aggregate(
  aggregation, "zips", StatePopulation.class);

Les résultats d'agrégation la classe implémente Iterable et donc nous pouvons itérer dessus et imprimer les résultats.

Si le modèle de données de sortie n'est pas connu, la classe MongoDB standard Document peut être utilisé.

3.2. Obtenir le plus petit État par population moyenne de la ville

Pour ce problème, nous aurons besoin de quatre étapes :

  1. $group additionner la population totale de chaque ville
  2. $group pour calculer la population moyenne de chaque état
  3. $sort étape pour classer les états par leur population urbaine moyenne par ordre croissant
  4. $limit pour obtenir le premier État avec la population urbaine moyenne la plus faible

Bien que ce ne soit pas nécessairement obligatoire, nous utiliserons un $project supplémentaire étape pour reformater le document selon StatePopulation modèle de données.

GroupOperation sumTotalCityPop = group("state", "city")
  .sum("pop").as("cityPop");
GroupOperation averageStatePop = group("_id.state")
  .avg("cityPop").as("avgCityPop");
SortOperation sortByAvgPopAsc = sort(Sort.by(Direction.ASC, "avgCityPop"));
LimitOperation limitToOnlyFirstDoc = limit(1);
ProjectionOperation projectToMatchModel = project()
  .andExpression("_id").as("state")
  .andExpression("avgCityPop").as("statePop");

Aggregation aggregation = newAggregation(
  sumTotalCityPop, averageStatePop, sortByAvgPopAsc,
  limitToOnlyFirstDoc, projectToMatchModel);

AggregationResults<StatePopulation> result = mongoTemplate
  .aggregate(aggregation, "zips", StatePopulation.class);
StatePopulation smallestState = result.getUniqueMappedResult();

Dans cet exemple, nous savons déjà qu'il n'y aura qu'un seul document dans le résultat puisque nous limitons le nombre de documents de sortie à 1 dans la dernière étape. En tant que tel, nous pouvons invoquer getUniqueMappedResult() pour obtenir la StatePopulation requise exemple.

Une autre chose à remarquer est qu'au lieu de compter sur le @Id annotation à la carte _id pour dire, nous l'avons fait explicitement dans la phase de projection.

3.3. Obtenez l'état avec les codes postaux maximum et minimum

Pour cet exemple, nous avons besoin de trois étapes :

  1. $groupe pour compter le nombre de codes postaux pour chaque état
  2. $sort pour ordonner les états par le nombre de codes postaux
  3. $groupe pour trouver l'état avec les codes postaux max et min en utilisant $first et $dernier opérateurs
GroupOperation sumZips = group("state").count().as("zipCount");
SortOperation sortByCount = sort(Direction.ASC, "zipCount");
GroupOperation groupFirstAndLast = group().first("_id").as("minZipState")
  .first("zipCount").as("minZipCount").last("_id").as("maxZipState")
  .last("zipCount").as("maxZipCount");

Aggregation aggregation = newAggregation(sumZips, sortByCount, groupFirstAndLast);

AggregationResults<Document> result = mongoTemplate
  .aggregate(aggregation, "zips", Document.class);
Document document= result.getUniqueMappedResult();

Ici, nous n'avons utilisé aucun modèle mais utilisé le Document déjà fourni avec le pilote MongoDB.