Les requêtes d’agrégation
Contents
5. Les requêtes d’agrégation¶
5.1. Regroupements¶
Auteurs/trices : CASTRIQUE Jérémy, NOJAC Dimitri, VAVASSEUR Salomé
Dans cette partie, nous allons étudier les regroupements dans les requêtes d’aggrégation. Dans un premier temps, nous étudierons ce qu’est l’étape de regroupement. Ensuite nous regarderons comment effectuer des calculs à l’aide des 4 d’opérateurs qui sont : $sum
, $max
, $min
, $count
avec ou sans groupe.
Les requêtes de regroupement vont permettre d’effectuer des opérations d’accumulation sur des documents regroupés. Il est l’équivalent de l’opérateur GROUP BY
en SQL.
Syntaxe
db.coll.aggregate([
{$group: {_id: <expression>, // Group By Expression
<field1>: { <accumulator1> : <expression1> },
...}
}
])
Pour tous les opérateurs que nous allons étudier dans cette partie du cours, la syntaxe sera identique à celle-ci.
5.1.1. Opérateur de somme¶
L’équivalent en SQL de l’opérateur $sum
est SUM
qui permet de sommer les valeurs prises pour un attribut.
Exemple de requête sans regroupement
db.coll.aggregate([
{$group: {_id: null,
nb: {$sum: "$att"}}}
])
SELECT SUM(att) as nb
FROM t
Vous remarquez qu’ici l’expression qui guide le regroupement est null
, soit une constante.
Cela signifie que tous les documents de la base seront regroupés ensemble et qu’un seul calcul sera fait pour ce groupe.
Exemple de requête avec regroupement
Pour créer des groupes d’individus, il faut indiquer comment former ces groupes dans l’identifiant.
Sur la base de food
, on peut par exemple filtrer par quartier.
Voici un exemple de requête :
En mongoDB :
use food
switched to db food
db.NYfood.aggregate([
{$group: {_id: "$borough",
nb: {$sum: {$size: "$grades"}}}}
])
{ "_id" : "Manhattan", "nb" : 38617 }
{ "_id" : "Missing", "nb" : 79 }
{ "_id" : "Brooklyn", "nb" : 21962 }
{ "_id" : "Bronx", "nb" : 8706 }
{ "_id" : "Queens", "nb" : 20877 }
{ "_id" : "Staten Island", "nb" : 3216 }
En SQL :
SELECT SUM(n_grades) as nb
FROM NYfood
GROUP BY borough
Dans cette requête, MongoDB va regrouper les individus ayant la même valweur pour l’attribut spécifié comme _id
et donc considérer les restaurants d’un même quartier ensemble.
L’opérateur $sum
permet de calculer et de retourner les sommes de variables numériques (ici les sommes des nombres de notes).
Attention
Il ne prend pas en compte les variables non numériques.
Syntaxe
db.coll.aggregate([
{$group: {_id: { <var>,
somme :{ $sum: [ <expression1>, <expression2> ... ]}}}
}
])
Autre exemple
Pour cette partie, nous allons nous placer dans cette base que nous avons créée.
{ "_id" : 1, "objet" : "a", "prix" : 10, "quantité" : 2},
{ "_id" : 2, "objet" : "b", "prix" : 20, "quantité" : 1},
{ "_id" : 3, "objet" : "c", "prix" : 5, "quantité" : 5},
{ "_id" : 4, "objet" : "a", "prix" : 10, "quantité" : 10},
{ "_id" : 5, "objet" : "c", "prix" : 5, "quantité" : 10}
Sans regroupement
Jusqu’ici, nous avons compté le nombre d’individus grâce à l’attribu $sum
, mais celui ci permet aussi d'additionner des variables
.
On se place maintenant dans la collection précédente.
db.coll.aggregate([
{$group: {_id: null,
qtt_tot: {$sum: "$quantité"}}}
])
SELECT SUM(quantité) AS qtt_tot
FROM coll
Résultat obtenu :
/* 1 */
{
"_id" : null,
"qtt_tot" : 28.0
}
Ici, on calcule la somme des quantités vendues.
Avec regroupement Si on veut sélectionner les sommes des durées de films par genre, il suffit de rajouter un regroupement comme le suivant :
db.coll.aggregate([
{$group: {_id: "$prix",
qtt_tot: {$sum: "$quantité"}}}
])
SELECT SUM(quantité) AS qtt_tot
FROM coll
GROUP BY prix
Résultat obtenu :
/* 1 */
{
"_id" : 20.0,
"qtt_tot" : 1.0
}
/* 2 */
{
"_id" : 10.0,
"qtt_tot" : 12.0
}
/* 3 */
{
"_id" : 5.0,
"qtt_tot" : 15.0
}
5.1.2. Opérateur de comptage¶
L’opérateur $count
renvoie le nombre de documents présents dans l’aggrégation.
Dans cet exemple, on assigne à la valeur NB_+24 le nombre de documents ayant un individu avec un âge supérieur à 24 :
use large_db
switched to db large_db
db.users.aggregate([
{$match: {"age": {$gt: 24}}},
{$count: "NB_+24"}
])
L’opérateur $match
exclu les documents qui possèdent un individu avec un âge <24.
L’opérateur $count
va donc agir sur les documents ayant un individu avec un âge supérieur à 24 à l’opérateur $gt
(plus grand que) et va assigner
à la valeur NB_+24 le nombre de documents répondant au critère.
Une autre façon (moins directe) d’effectuer des opérations de comptage est d’utiliser l’opérateur $sum
.
Sans regroupement Regardons une requête simple :
En mongoDB :
db.NYfood.aggregate([
{$group:{_id: null,
nb: {$sum: 1}}}
])
En SQL :
SELECT COUNT(*) as nb
FROM NYfood
On utilise la fonction aggregate.
Lorsqu’on utilise aggregate, il faut donner les individus sur lesquels on veut faire la requête.
Dans notre cas, on choisit tout les individus. On le note id: null
.
On créé notre variable qu’on appelle nb qui va faire la somme de tout les individus.
Dans cet exemple, nous avons compté le nombre d’individus sans sélection.
En pratique, cela n’a pas forcément beaucoup d’intérêt. Il s’avère plus utile de pouvoir sélectionner le nombre de variables répondant à un critère. Pour cela, nous allons regarder avec une requête de regroupement.
Avec regroupement Toujours dans la collection NYfood de la base food, on cherche à connaitre le nombre de restaurants par type de cuisine. Pour cela, on va effectuer un regroupement sur l’attribut “cuisine”.
En mongoDB :
db.NYfood.aggregate([
{$group:{_id: "$cuisine",
nb_par_cuis: {$sum: 1}}}
])
En SQL :
SELECT COUNT(*) AS nb_par_cuisine
FROM NYfood
GROUP BY cuisine
On obtient donc plusieurs listes différentes contenant pour chacune le nombre de restaurants dans chaque liste nommée par le type de cuisine.
Il y a donc eu un comptage du nombre de restaurants en fonction de la variable cuisine.
5.1.3. Opérateurs d’extremum¶
Syntaxe
db.coll.aggregate([
{$group:
{_id: { <var>,
max: {$max: [ <expression1>, <expression2> ... ]}}}
}
]
)
Pour cette partie on se basera sur cette collection pour les exemples :
{ "_id" : 1, "objet" : "a", "prix" : 10, "quantité" : 2},
{ "_id" : 2, "objet" : "b", "prix" : 20, "quantité" : 1},
{ "_id" : 3, "objet" : "c", "prix" : 5, "quantité" : 5},
{ "_id" : 4, "objet" : "a", "prix" : 10, "quantité" : 10},
{ "_id" : 5, "objet" : "c", "prix" : 5, "quantité" : 10}
Nous allons nous intéresser aux opérateurs $min
et $max
au sein de l’opéarteur $group
,
ils peuvent aussi être utilisés dans l’opérateur $project
que nous verrons en deuxième partie de chapitre.
En SQL, les équivalents sont les opérateurs MIN
et `MAX.
5.1.3.1. Sans regroupement¶
$min
et $max
s’ils sont utilisés sans regroupement retournent respectivement la valeur minimale et la valeur maximale
de l’attribut sur lequel ils sont appliqués et ceci sur tous les documents.
Exemple :
db.ventes.aggregate([
{$group: {_id:null,
prix_max: {$max: "$prix"},
prix_min: {$min: "$prix"}}}
])
SELECT MAX(prix) as "prix max", MIN(prix) as "prix min"
FROM ventes
Attention
Ne pas oublier le “$” dans les attributs entre guillemets à droite des deux points pour bien faire référence à l’attribut et non à une chaîne de caractères.
Cette requête renvoie la valeur maximale puis minimale que prend la variable $prix
sur tous les documents :
{
"_id" : null,
"prix_max" : 20.0,
"prix_min" : 5.0
}
5.1.3.2. Avec regroupement¶
On peut aussi réaliser un regroupement et ainsi $min
et $max
renvoient toujours la valeur minimale et la valeur maximale
de l’attribut sur lequel ils sont appliqués, mais cette fois-ci en étant appliqués sur les documents de l’ensemble de documents qui partagent la même clé de regroupement.
Exemple :
db.ventes.aggregate([
{$group: {_id:"$objet",
quantité_max: {$max: "$quantité"},
quantité_min: {$min: "$quantité"}}}
])
SELECT MAX(prix) as "prix max", MIN(prix) as "prix min"
FROM ventes
GROUP BY quantité
On groupe à l’aide de la clé objet
,
on renvoie donc la valeur maximale puis minimale que prend la variable quantité
pour chaque objet
différent :
{
"_id" : "a",
"quantité_max" : 10.0,
"quantité_min" : 2.0
}
/* 2 */
{
"_id" : "b",
"quantité_max" : 1.0,
"quantité_min" : 1.0
}
/* 3 */
{
"_id" : "c",
"quantité_max" : 10.0,
"quantité_min" : 5.0
}
Null ou inexistant
Si certains documents ont une valeur de type null ou qui n’existe pas pour l’attribut sur lequel on applique $min
ou $max
,
les opérateurs ne prennent pas en compte les valeurs de type null ou manquantes pour le calcul.
Si tous les documents ont une valeur de type null ou qui n’existe pas, les opérateurs renvoient null pour la valeur minimale
ou la valeur maximale.
5.2. Successions d’étapes d’agrégation¶
Auteurs/trices : Marine BINARD, Yann CAUSEUR, Arthur CONAS
5.2.1. Introduction¶
Les successions d’étapes d’agrégation vont permettre d’obtenir des requêtes proches de ce qu’on peut trouver en SQL.
Contrairement à SQL où l’ordre est pré-défini (SELECT FROM WHERE ORDER BY
), ici ce n’est pas le cas. Il n’empêche que l’ordre dans lequel on place
nos étapes est crucial.
Nos étapes peuvent toutes être effectuées une à une et indépendamment. En fait, à l’intérieur de notre db.coll.aggregate([])
, il y aura notre liste d’étapes,
contenues dans des crochets et séparées par des virgules, qui s’effectueront sur les données que l’étape d’avant aura rendue.
Il peut donc être intéressant d’éxécuter le code étape par étape pour savoir sur quelles données on travaille à un moment donné.
Dans la suite, nous présentons de nouvelles étapes d’agrégation.
5.2.2. Project¶
Pourquoi l’utiliser ?
Il peut arriver lors d’une requête d’agrégation de vouloir créer de nouvelles variables par exemple, pour des calculs. La commande $project
permet donc de créer de nouvelles variables. Néanmoins, il faut faire attention,
lorsque l’on crée une nouvelle variable dans une requête d’agrégation.
Tous les attributs déjà existants pour les documents d’une collection ne sont plus mémorisés. Donc, si on veut créer une nouvelle variable, tout en gardant celles déjà existantes, il faut le mentionner dans le $project
.
Comment ça fonctionne ?
Syntaxe :
db.coll.aggregate(
[
{$project : {<nom_nouv_att1> : <val_att1>, <nom_nouv_att2> : <val_att2>, ... }}
]
)
Le fait de vouloir garder un attribut déjà existant fonctionne de la même façon que la création, il faut donc renommer la variable existante.
Exemple :
use food
switched to db food
db.NYfood.aggregate(
[
{$project: {"n_notes" : {$size : '$grades'}}}
]
)
{ "_id" : ObjectId("62309ad619b1d4e452b4efcc"), "n_notes" : 4 }
{ "_id" : ObjectId("62309ad619b1d4e452b4efcd"), "n_notes" : 5 }
{ "_id" : ObjectId("62309ad619b1d4e452b4efce"), "n_notes" : 4 }
{ "_id" : ObjectId("62309ad619b1d4e452b4efcf"), "n_notes" : 4 }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd0"), "n_notes" : 4 }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd1"), "n_notes" : 4 }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd2"), "n_notes" : 4 }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd3"), "n_notes" : 6 }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd4"), "n_notes" : 5 }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd5"), "n_notes" : 4 }
Type "it" for more
Sur l’exemple ci-dessus, on vient créer une variable n_notes qui prend pour valeur la taille de la liste grades
(qui contient les différentes notes attribuées aux restaurants).
On cherche donc, ici, à compter le nombre de notes attribué à chaque restaurant.
Mais tous les autres attributs du restaurant sont effacés. Par la suite,
on ne pourra donc retrouver que le nombre de notes attribué et non le quartier
ou le type de restaurant.
Si on veut afficher le quartier en question, on doit le préciser tel que :
db.NYfood.aggregate(
[
{$project: {"n_notes" : {$size : '$grades'}, quartier :'$borough'}}
]
)
{ "_id" : ObjectId("62309ad619b1d4e452b4efcc"), "n_notes" : 4, "quartier" : "Brooklyn" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efcd"), "n_notes" : 5, "quartier" : "Bronx" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efce"), "n_notes" : 4, "quartier" : "Manhattan" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efcf"), "n_notes" : 4, "quartier" : "Brooklyn" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd0"), "n_notes" : 4, "quartier" : "Queens" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd1"), "n_notes" : 4, "quartier" : "Staten Island" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd2"), "n_notes" : 4, "quartier" : "Queens" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd3"), "n_notes" : 6, "quartier" : "Brooklyn" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd4"), "n_notes" : 5, "quartier" : "Brooklyn" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd5"), "n_notes" : 4, "quartier" : "Brooklyn" }
Type "it" for more
Avec cette requête, on peut voir le quartier du restaurant. Par ailleurs, la variable borough
a été renommée quartier
. On peut également conserver cette
variable sans la renommer avec cette syntaxe.
db.NYfood.aggregate(
[
{$project: {"n_notes" : {$size : '$grades'}, borough : 1}}
]
)
{ "_id" : ObjectId("62309ad619b1d4e452b4efcc"), "borough" : "Brooklyn", "n_notes" : 4 }
{ "_id" : ObjectId("62309ad619b1d4e452b4efcd"), "borough" : "Bronx", "n_notes" : 5 }
{ "_id" : ObjectId("62309ad619b1d4e452b4efce"), "borough" : "Manhattan", "n_notes" : 4 }
{ "_id" : ObjectId("62309ad619b1d4e452b4efcf"), "borough" : "Brooklyn", "n_notes" : 4 }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd0"), "borough" : "Queens", "n_notes" : 4 }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd1"), "borough" : "Staten Island", "n_notes" : 4 }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd2"), "borough" : "Queens", "n_notes" : 4 }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd3"), "borough" : "Brooklyn", "n_notes" : 6 }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd4"), "borough" : "Brooklyn", "n_notes" : 5 }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd5"), "borough" : "Brooklyn", "n_notes" : 4 }
Type "it" for more
Traduction SQL :
L’équivalent en SQL de la commande $project
est l’étape SELECT
et AS
qui
permettent de créer de nouvelles variables. Par contre, en SQL,
l’étape AS
est facultative, la nouvelle variable prendra
comme nom la formule du calcul. En MongoDB, elle est obligatoire !
Si on ne précise pas le nom de la nouvelle variable, cela affichera
une erreur. Pour la traduction SQL de l’exemple précédent, il convient de faire attention.
Pour rappel, les listes n’existent pas en SQL ! D’où la nécessité
dans certains moments de faire des calculs verticaux, ce qui n’est pas nécéssaire.
Dans notre cas, en SQL l’attribut grades
serait une table à part entière (avec toutes les notes grade
, une clé étrangère faisant référence au restaurant)
Il faudrait donc faire une jointure sur celle-ci puis grouper par restaurant (en imaginant qu’il existe un ID pour chaque restaurant)
L’exemple serait donc:
SELECT borough
FROM NYfood NATURAL JOIN grades
GROUP BY restoID
HAVING COUNT(grade) AS "n_notes"
5.2.3. Sort¶
Pourquoi l’utiliser ?
Comme dans la plus part des langages de bases de données, MongoDB ne stocke pas les documents dans une collection dans un ordre
en particulier. C’est pourquoi l’étape sort
(tri en français) va permettre
de trier l’ensemble de tous les documents d’entrée afin de les renvoyer dans
l’ordre choisi par l’utilisateur. Nous pouvons les trier dans l’ordre croissant,
décroissant, chronologique ou bien alphabétique selon le type du champ
souhaitant être trié.
Il est possible de trier sur plusieurs champs à la fois, mais dans ce cas l’ordre de tri est évalué de gauche à droite.
Le $sort
est finalement l’équivalent du ORDER BY
en SQL.
Comment ça fonctionne ?
Syntaxe :
db.coll.aggregate(
[
{$sort: {<champ1>: <sort order>, <champ2>: <sort order> ...}}
]
)
Le <sort order>
peut prendre la valeur : 1 (croissant), -1 (décroissant) ou encore {$meta: "textScore"}
(il s’agit d’un tri de métadonnées textScore calculées dans l’ordre décroissant).
Exemples :
Attention à bien prendre en compte le fait que lors du tri sur un champ contenant des valeurs en double (ou non unique), les documents contenant ces valeurs peuvent être renvoyés dans n’importe quel ordre.
db.NYfood.aggregate(
[
{$sort : {borough : 1}}
]
)
{ "_id" : ObjectId("62309ad619b1d4e452b4efcd"), "address" : { "building" : "1007", "loc" : { "type" : "Point", "coordinates" : [ -73.856077, 40.848447 ] }, "street" : "Morris Park Ave", "zipcode" : "10462" }, "borough" : "Bronx", "cuisine" : "Bakery", "grades" : [ { "date" : ISODate("2014-03-03T00:00:00Z"), "grade" : "A", "score" : 2 }, { "date" : ISODate("2013-09-11T00:00:00Z"), "grade" : "A", "score" : 6 }, { "date" : ISODate("2013-01-24T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2011-11-23T00:00:00Z"), "grade" : "A", "score" : 9 }, { "date" : ISODate("2011-03-10T00:00:00Z"), "grade" : "B", "score" : 14 } ], "name" : "Morris Park Bake Shop", "restaurant_id" : "30075445" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd7"), "address" : { "building" : "2300", "loc" : { "type" : "Point", "coordinates" : [ -73.8786113, 40.8502883 ] }, "street" : "Southern Boulevard", "zipcode" : "10460" }, "borough" : "Bronx", "cuisine" : "American ", "grades" : [ { "date" : ISODate("2014-05-28T00:00:00Z"), "grade" : "A", "score" : 11 }, { "date" : ISODate("2013-06-19T00:00:00Z"), "grade" : "A", "score" : 4 }, { "date" : ISODate("2012-06-15T00:00:00Z"), "grade" : "A", "score" : 3 } ], "name" : "Wild Asia", "restaurant_id" : "40357217" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efec"), "address" : { "building" : "1006", "loc" : { "type" : "Point", "coordinates" : [ -73.84856870000002, 40.8903781 ] }, "street" : "East 233 Street", "zipcode" : "10466" }, "borough" : "Bronx", "cuisine" : "Ice Cream, Gelato, Yogurt, Ices", "grades" : [ { "date" : ISODate("2014-04-24T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2013-09-05T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2013-02-21T00:00:00Z"), "grade" : "A", "score" : 9 }, { "date" : ISODate("2012-07-03T00:00:00Z"), "grade" : "A", "score" : 11 }, { "date" : ISODate("2011-07-11T00:00:00Z"), "grade" : "A", "score" : 5 } ], "name" : "Carvel Ice Cream", "restaurant_id" : "40363093" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efee"), "address" : { "building" : "1236", "loc" : { "type" : "Point", "coordinates" : [ -73.8893654, 40.81376179999999 ] }, "street" : "238 Spofford Ave", "zipcode" : "10474" }, "borough" : "Bronx", "cuisine" : "Chinese", "grades" : [ { "date" : ISODate("2013-12-30T00:00:00Z"), "grade" : "A", "score" : 8 }, { "date" : ISODate("2013-01-08T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2012-06-12T00:00:00Z"), "grade" : "B", "score" : 15 } ], "name" : "Happy Garden", "restaurant_id" : "40363289" }
{ "_id" : ObjectId("62309ad619b1d4e452b4f001"), "address" : { "building" : "277", "loc" : { "type" : "Point", "coordinates" : [ -73.8941893, 40.8634684 ] }, "street" : "East Kingsbridge Road", "zipcode" : "10458" }, "borough" : "Bronx", "cuisine" : "Chinese", "grades" : [ { "date" : ISODate("2014-03-03T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2013-09-26T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2013-03-19T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2012-08-29T00:00:00Z"), "grade" : "A", "score" : 11 }, { "date" : ISODate("2011-08-17T00:00:00Z"), "grade" : "A", "score" : 13 } ], "name" : "Happy Garden", "restaurant_id" : "40364296" }
{ "_id" : ObjectId("62309ad619b1d4e452b4f009"), "address" : { "building" : "658", "loc" : { "type" : "Point", "coordinates" : [ -73.81363999999999, 40.82941100000001 ] }, "street" : "Clarence Ave", "zipcode" : "10465" }, "borough" : "Bronx", "cuisine" : "American ", "grades" : [ { "date" : ISODate("2014-06-21T00:00:00Z"), "grade" : "A", "score" : 5 }, { "date" : ISODate("2012-07-11T00:00:00Z"), "grade" : "A", "score" : 10 } ], "name" : "Manhem Club", "restaurant_id" : "40364363" }
{ "_id" : ObjectId("62309ad619b1d4e452b4f021"), "address" : { "building" : "2222", "loc" : { "type" : "Point", "coordinates" : [ -73.84971759999999, 40.8304811 ] }, "street" : "Haviland Avenue", "zipcode" : "10462" }, "borough" : "Bronx", "cuisine" : "American ", "grades" : [ { "date" : ISODate("2014-12-18T00:00:00Z"), "grade" : "A", "score" : 7 }, { "date" : ISODate("2014-05-01T00:00:00Z"), "grade" : "B", "score" : 17 }, { "date" : ISODate("2013-03-14T00:00:00Z"), "grade" : "A", "score" : 12 }, { "date" : ISODate("2012-09-20T00:00:00Z"), "grade" : "A", "score" : 9 }, { "date" : ISODate("2012-02-08T00:00:00Z"), "grade" : "B", "score" : 19 } ], "name" : "The New Starling Athletic Club Of The Bronx", "restaurant_id" : "40364956" }
{ "_id" : ObjectId("62309ad619b1d4e452b4f03a"), "address" : { "building" : "72", "loc" : { "type" : "Point", "coordinates" : [ -73.92506, 40.8275556 ] }, "street" : "East 161 Street", "zipcode" : "10451" }, "borough" : "Bronx", "cuisine" : "American ", "grades" : [ { "date" : ISODate("2014-04-15T00:00:00Z"), "grade" : "A", "score" : 9 }, { "date" : ISODate("2013-11-14T00:00:00Z"), "grade" : "A", "score" : 4 }, { "date" : ISODate("2013-07-29T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2012-12-31T00:00:00Z"), "grade" : "B", "score" : 15 }, { "date" : ISODate("2012-05-30T00:00:00Z"), "grade" : "A", "score" : 13 }, { "date" : ISODate("2012-01-09T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2011-08-15T00:00:00Z"), "grade" : "C", "score" : 37 } ], "name" : "Yankee Tavern", "restaurant_id" : "40365499" }
{ "_id" : ObjectId("62309ad619b1d4e452b4f04c"), "address" : { "building" : "331", "loc" : { "type" : "Point", "coordinates" : [ -73.87786539999999, 40.8724377 ] }, "street" : "East 204 Street", "zipcode" : "10467" }, "borough" : "Bronx", "cuisine" : "Irish", "grades" : [ { "date" : ISODate("2014-08-26T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2014-03-26T00:00:00Z"), "grade" : "B", "score" : 23 }, { "date" : ISODate("2013-09-11T00:00:00Z"), "grade" : "A", "score" : 13 }, { "date" : ISODate("2012-12-18T00:00:00Z"), "grade" : "B", "score" : 27 }, { "date" : ISODate("2011-10-20T00:00:00Z"), "grade" : "A", "score" : 13 } ], "name" : "Mcdwyers Pub", "restaurant_id" : "40365893" }
{ "_id" : ObjectId("62309ad619b1d4e452b4f065"), "address" : { "building" : "5820", "loc" : { "type" : "Point", "coordinates" : [ -73.9002615, 40.885186 ] }, "street" : "Broadway", "zipcode" : "10463" }, "borough" : "Bronx", "cuisine" : "American ", "grades" : [ { "date" : ISODate("2014-02-26T00:00:00Z"), "grade" : "A", "score" : 5 }, { "date" : ISODate("2013-10-09T00:00:00Z"), "grade" : "B", "score" : 19 }, { "date" : ISODate("2013-05-15T00:00:00Z"), "grade" : "A", "score" : 9 }, { "date" : ISODate("2012-11-20T00:00:00Z"), "grade" : "B", "score" : 18 }, { "date" : ISODate("2011-10-17T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2011-06-22T00:00:00Z"), "grade" : "C", "score" : 35 } ], "name" : "The Punch Bowl", "restaurant_id" : "40366497" }
Type "it" for more
Traduction SQL :
SELECT borough
FROM NYfood
ORDER BY borough
En effet, dans l’exemple ci-dessus, le champ quartier n’est pas un champ avec des valeurs uniques. Si un ordre de tri cohérent est souhaité, il est important d’au moins inclure un champ dans votre tri qui contient des valeurs uniques. Généralement, le moyen le plus simple de garantir cela consiste à inclure le champ _id dans la requête de tri.
db.NYfood.aggregate(
[
{$sort : {borough : 1, _id : 1}}
]
)
{ "_id" : ObjectId("62309ad619b1d4e452b4efcd"), "address" : { "building" : "1007", "loc" : { "type" : "Point", "coordinates" : [ -73.856077, 40.848447 ] }, "street" : "Morris Park Ave", "zipcode" : "10462" }, "borough" : "Bronx", "cuisine" : "Bakery", "grades" : [ { "date" : ISODate("2014-03-03T00:00:00Z"), "grade" : "A", "score" : 2 }, { "date" : ISODate("2013-09-11T00:00:00Z"), "grade" : "A", "score" : 6 }, { "date" : ISODate("2013-01-24T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2011-11-23T00:00:00Z"), "grade" : "A", "score" : 9 }, { "date" : ISODate("2011-03-10T00:00:00Z"), "grade" : "B", "score" : 14 } ], "name" : "Morris Park Bake Shop", "restaurant_id" : "30075445" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd7"), "address" : { "building" : "2300", "loc" : { "type" : "Point", "coordinates" : [ -73.8786113, 40.8502883 ] }, "street" : "Southern Boulevard", "zipcode" : "10460" }, "borough" : "Bronx", "cuisine" : "American ", "grades" : [ { "date" : ISODate("2014-05-28T00:00:00Z"), "grade" : "A", "score" : 11 }, { "date" : ISODate("2013-06-19T00:00:00Z"), "grade" : "A", "score" : 4 }, { "date" : ISODate("2012-06-15T00:00:00Z"), "grade" : "A", "score" : 3 } ], "name" : "Wild Asia", "restaurant_id" : "40357217" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efec"), "address" : { "building" : "1006", "loc" : { "type" : "Point", "coordinates" : [ -73.84856870000002, 40.8903781 ] }, "street" : "East 233 Street", "zipcode" : "10466" }, "borough" : "Bronx", "cuisine" : "Ice Cream, Gelato, Yogurt, Ices", "grades" : [ { "date" : ISODate("2014-04-24T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2013-09-05T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2013-02-21T00:00:00Z"), "grade" : "A", "score" : 9 }, { "date" : ISODate("2012-07-03T00:00:00Z"), "grade" : "A", "score" : 11 }, { "date" : ISODate("2011-07-11T00:00:00Z"), "grade" : "A", "score" : 5 } ], "name" : "Carvel Ice Cream", "restaurant_id" : "40363093" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efee"), "address" : { "building" : "1236", "loc" : { "type" : "Point", "coordinates" : [ -73.8893654, 40.81376179999999 ] }, "street" : "238 Spofford Ave", "zipcode" : "10474" }, "borough" : "Bronx", "cuisine" : "Chinese", "grades" : [ { "date" : ISODate("2013-12-30T00:00:00Z"), "grade" : "A", "score" : 8 }, { "date" : ISODate("2013-01-08T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2012-06-12T00:00:00Z"), "grade" : "B", "score" : 15 } ], "name" : "Happy Garden", "restaurant_id" : "40363289" }
{ "_id" : ObjectId("62309ad619b1d4e452b4f001"), "address" : { "building" : "277", "loc" : { "type" : "Point", "coordinates" : [ -73.8941893, 40.8634684 ] }, "street" : "East Kingsbridge Road", "zipcode" : "10458" }, "borough" : "Bronx", "cuisine" : "Chinese", "grades" : [ { "date" : ISODate("2014-03-03T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2013-09-26T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2013-03-19T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2012-08-29T00:00:00Z"), "grade" : "A", "score" : 11 }, { "date" : ISODate("2011-08-17T00:00:00Z"), "grade" : "A", "score" : 13 } ], "name" : "Happy Garden", "restaurant_id" : "40364296" }
{ "_id" : ObjectId("62309ad619b1d4e452b4f009"), "address" : { "building" : "658", "loc" : { "type" : "Point", "coordinates" : [ -73.81363999999999, 40.82941100000001 ] }, "street" : "Clarence Ave", "zipcode" : "10465" }, "borough" : "Bronx", "cuisine" : "American ", "grades" : [ { "date" : ISODate("2014-06-21T00:00:00Z"), "grade" : "A", "score" : 5 }, { "date" : ISODate("2012-07-11T00:00:00Z"), "grade" : "A", "score" : 10 } ], "name" : "Manhem Club", "restaurant_id" : "40364363" }
{ "_id" : ObjectId("62309ad619b1d4e452b4f021"), "address" : { "building" : "2222", "loc" : { "type" : "Point", "coordinates" : [ -73.84971759999999, 40.8304811 ] }, "street" : "Haviland Avenue", "zipcode" : "10462" }, "borough" : "Bronx", "cuisine" : "American ", "grades" : [ { "date" : ISODate("2014-12-18T00:00:00Z"), "grade" : "A", "score" : 7 }, { "date" : ISODate("2014-05-01T00:00:00Z"), "grade" : "B", "score" : 17 }, { "date" : ISODate("2013-03-14T00:00:00Z"), "grade" : "A", "score" : 12 }, { "date" : ISODate("2012-09-20T00:00:00Z"), "grade" : "A", "score" : 9 }, { "date" : ISODate("2012-02-08T00:00:00Z"), "grade" : "B", "score" : 19 } ], "name" : "The New Starling Athletic Club Of The Bronx", "restaurant_id" : "40364956" }
{ "_id" : ObjectId("62309ad619b1d4e452b4f03a"), "address" : { "building" : "72", "loc" : { "type" : "Point", "coordinates" : [ -73.92506, 40.8275556 ] }, "street" : "East 161 Street", "zipcode" : "10451" }, "borough" : "Bronx", "cuisine" : "American ", "grades" : [ { "date" : ISODate("2014-04-15T00:00:00Z"), "grade" : "A", "score" : 9 }, { "date" : ISODate("2013-11-14T00:00:00Z"), "grade" : "A", "score" : 4 }, { "date" : ISODate("2013-07-29T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2012-12-31T00:00:00Z"), "grade" : "B", "score" : 15 }, { "date" : ISODate("2012-05-30T00:00:00Z"), "grade" : "A", "score" : 13 }, { "date" : ISODate("2012-01-09T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2011-08-15T00:00:00Z"), "grade" : "C", "score" : 37 } ], "name" : "Yankee Tavern", "restaurant_id" : "40365499" }
{ "_id" : ObjectId("62309ad619b1d4e452b4f04c"), "address" : { "building" : "331", "loc" : { "type" : "Point", "coordinates" : [ -73.87786539999999, 40.8724377 ] }, "street" : "East 204 Street", "zipcode" : "10467" }, "borough" : "Bronx", "cuisine" : "Irish", "grades" : [ { "date" : ISODate("2014-08-26T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2014-03-26T00:00:00Z"), "grade" : "B", "score" : 23 }, { "date" : ISODate("2013-09-11T00:00:00Z"), "grade" : "A", "score" : 13 }, { "date" : ISODate("2012-12-18T00:00:00Z"), "grade" : "B", "score" : 27 }, { "date" : ISODate("2011-10-20T00:00:00Z"), "grade" : "A", "score" : 13 } ], "name" : "Mcdwyers Pub", "restaurant_id" : "40365893" }
{ "_id" : ObjectId("62309ad619b1d4e452b4f065"), "address" : { "building" : "5820", "loc" : { "type" : "Point", "coordinates" : [ -73.9002615, 40.885186 ] }, "street" : "Broadway", "zipcode" : "10463" }, "borough" : "Bronx", "cuisine" : "American ", "grades" : [ { "date" : ISODate("2014-02-26T00:00:00Z"), "grade" : "A", "score" : 5 }, { "date" : ISODate("2013-10-09T00:00:00Z"), "grade" : "B", "score" : 19 }, { "date" : ISODate("2013-05-15T00:00:00Z"), "grade" : "A", "score" : 9 }, { "date" : ISODate("2012-11-20T00:00:00Z"), "grade" : "B", "score" : 18 }, { "date" : ISODate("2011-10-17T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2011-06-22T00:00:00Z"), "grade" : "C", "score" : 35 } ], "name" : "The Punch Bowl", "restaurant_id" : "40366497" }
Type "it" for more
Cette fois ci, la requête affichera l’ensemble de la collection avec les noms de quartier affichés par ordre alphabétique. Les collections du quartier de “Bronx” seront les premières à être affichées, puis ensuite l’ordre par identifiant sera conservé lorsque le nom de quartier sera le même pour plusieurs collections.
Traduction SQL :
SELECT borough
FROM NYfood
ORDER BY borough, _id
5.2.4. Limit¶
Pourquoi l’utiliser ?
L’étape $limit
va simplement permettre de
limiter le nombre de documents voulant être
affichés par la requête. Il n’y a pas grand
intérêt à utiliser le limit tout seul. Généralement,
il est utilisé avec l’étape $sort
vu précédemment.
Comment ça fonctionne ?
Syntaxe :
db.coll.aggregate(
[
{$limit : 5}
]
)
L’argument qui est pris par le $limit
est toujours
un entier positif, qui va déterminer le nombre
de collections que l’on souhaite afficher.
Exemple
Dans cet exemple, on souhaite afficher les 3 quartiers possédant le plus de restaurants.
db.NYfood.aggregate(
[
{$group: {_id: "$borough", nb: {$sum: 1}}},
{$sort: {nb: -1}},
{$limit: 3}
]
)
{ "_id" : "Manhattan", "nb" : 10258 }
{ "_id" : "Brooklyn", "nb" : 6085 }
{ "_id" : "Queens", "nb" : 5656 }
On remarque ici que nous ne pouvons pas utiliser
l’étape $limit
seul sans le sort.
Nous avons d’abord besoin de trier le nombre
de restaurants par ordre décroissant puis enfin
préciser que nous souhaitons obtenir seulement les
3 premiers quartiers contenant le plus de restaurants.
Traduction SQL :
SELECT count(borough)
FROM NYfood
GROUP BY borough
ORDER BY count(borough) desc
LIMIT 3
5.2.5. Match¶
Pourquoi l’utiliser
$match
peut être utilisé comme un filtre, avec une condition. On pourrait le mettre n’importe où dans notre requête mais il est particulièrement intéressant en début ou en fin de requête.
Comment ça fonctionne ?
Syntaxe :
Le $match
est un requête du type de celles qu’on passe à find
.
Exemple :
db.NYfood.aggregate(
[
{$match: {"borough": 'Brooklyn'}},
{$unwind: "$grades"},
{$group : {_id: "$grades.grade",
n:{$sum:1}
}
},
{$match:{n:{$gt:1000}}},
]
)
{ "_id" : "A", "n" : 17324 }
{ "_id" : "B", "n" : 3055 }
SELECT COUNT(grade) as n
FROM NYfood
WHERE Borough='Brooklyn'
GROUP BY grade
HAVING n > 1000
Ici le premier $match
sert comme un WHERE
,
et le deuxième comme un HAVING
en SQL.
Dans tous les cas, le $match
fait une sélection sur le jeu de données en fonction d’une condition, au moment où il est placé.
Si le $match
est au début, il fera une sélection sur l’ensemble des données (ici NYfood
) mais n’aura pas accès aux opérations qui sont effectuées après (comme le $group
dans notre cas).
C’est pour cela qu’on utilise aussi le $match
plus tard, pour avoir accès aux données créées avec nos bouts de requêtes précédents, ce qui permet ici d’avoir accès au n
.
Cependant, ce dernier $match
n’a pas accès à toute la base de données NYfood
et n’agit que sur les résultats des requêtes précédentes.
5.2.6. Unwind¶
Pourquoi l’utiliser ?
Il arrive que les documents de certaines collections possèdent pour attribut une liste. Lorsque l’on effectue une requête d’agrégation, il peut être nécéssaire d’agir non pas sur la liste mais sur chaque élément de la liste. Pour cela, on utilise la commande $unwind
. Elle permet, pour chaque élément de la liste, de dupliquer le document pour chaque valeur de la liste.
Comment ça fonctionne ?
Syntaxe :
db.coll.aggregate(
[
{$unwind : "$att"}}
]
)
En général, un $unwid
seul n’a peu d’intérêt.$att
est une liste de taille 10 que la collection comporte 1000 individus, la requête d’exemple renverra un résultat de 10 000 lignes (10 * 1000)
Exemple
db.NYfood.aggregate(
[
{$unwind :"$grades"},
{$group: {_id : '$grades.grade',
n: {$sum:1}}
},
]
)
{ "_id" : "Z", "n" : 1337 }
{ "_id" : "B", "n" : 12602 }
{ "_id" : "P", "n" : 1197 }
{ "_id" : "Not Yet Graded", "n" : 524 }
{ "_id" : "C", "n" : 3145 }
{ "_id" : "A", "n" : 74652 }
Voici un exemple concret d’utilisation d’un $unwind
. Dans la requête, on cherche à compter le nombre de A ayant été attribués à l’ensemble des restaurants de la collection, puis le nombre de B, C ….
Pour que cette requête fonctionne, le $unwind
est obligatoire, sinon on considère la liste entière des notes et on ne peut donc pas compter.
Traduction SQL :
Il n’existe pas réellement d’équivalent SQL au $unwind
. Néanmoins, il se rapproche d’une opération de jointure sans aucun filtre.
5.2.7. Quelques requêtes pour tout comprendre¶
Afin d’illustrer le fonctionnement pas à pas, découpons une requête en détail.
Pour cet exemple, on veut les 3 notes les plus données dans les restaurants du quartier de Brooklyn.
La première étape naturelle est de sélectionner les restaurants présents uniquement dans le quartier de Brooklyn.
Pour cela on utilise $match
, qui retourne uniquement les restaurants de Brooklyn.
db.NYfood.aggregate(
[
{$match: {"borough": "Brooklyn"}},
]
)
{ "_id" : ObjectId("62309ad619b1d4e452b4efcc"), "address" : { "building" : "469", "loc" : { "type" : "Point", "coordinates" : [ -73.961704, 40.662942 ] }, "street" : "Flatbush Avenue", "zipcode" : "11225" }, "borough" : "Brooklyn", "cuisine" : "Hamburgers", "grades" : [ { "date" : ISODate("2014-12-30T00:00:00Z"), "grade" : "A", "score" : 8 }, { "date" : ISODate("2014-07-01T00:00:00Z"), "grade" : "B", "score" : 23 }, { "date" : ISODate("2013-04-30T00:00:00Z"), "grade" : "A", "score" : 12 }, { "date" : ISODate("2012-05-08T00:00:00Z"), "grade" : "A", "score" : 12 } ], "name" : "Wendy'S", "restaurant_id" : "30112340" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efcf"), "address" : { "building" : "2780", "loc" : { "type" : "Point", "coordinates" : [ -73.98241999999999, 40.579505 ] }, "street" : "Stillwell Avenue", "zipcode" : "11224" }, "borough" : "Brooklyn", "cuisine" : "American ", "grades" : [ { "date" : ISODate("2014-06-10T00:00:00Z"), "grade" : "A", "score" : 5 }, { "date" : ISODate("2013-06-05T00:00:00Z"), "grade" : "A", "score" : 7 }, { "date" : ISODate("2012-04-13T00:00:00Z"), "grade" : "A", "score" : 12 }, { "date" : ISODate("2011-10-12T00:00:00Z"), "grade" : "A", "score" : 12 } ], "name" : "Riviera Caterer", "restaurant_id" : "40356018" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd3"), "address" : { "building" : "7114", "loc" : { "type" : "Point", "coordinates" : [ -73.9068506, 40.6199034 ] }, "street" : "Avenue U", "zipcode" : "11234" }, "borough" : "Brooklyn", "cuisine" : "Delicatessen", "grades" : [ { "date" : ISODate("2014-05-29T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2014-01-14T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2013-08-03T00:00:00Z"), "grade" : "A", "score" : 8 }, { "date" : ISODate("2012-07-18T00:00:00Z"), "grade" : "A", "score" : 10 }, { "date" : ISODate("2012-03-09T00:00:00Z"), "grade" : "A", "score" : 13 }, { "date" : ISODate("2011-10-14T00:00:00Z"), "grade" : "A", "score" : 9 } ], "name" : "Wilken'S Fine Food", "restaurant_id" : "40356483" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd4"), "address" : { "building" : "6409", "loc" : { "type" : "Point", "coordinates" : [ -74.00528899999999, 40.628886 ] }, "street" : "11 Avenue", "zipcode" : "11219" }, "borough" : "Brooklyn", "cuisine" : "American ", "grades" : [ { "date" : ISODate("2014-07-18T00:00:00Z"), "grade" : "A", "score" : 12 }, { "date" : ISODate("2013-07-30T00:00:00Z"), "grade" : "A", "score" : 12 }, { "date" : ISODate("2013-02-13T00:00:00Z"), "grade" : "A", "score" : 11 }, { "date" : ISODate("2012-08-16T00:00:00Z"), "grade" : "A", "score" : 2 }, { "date" : ISODate("2011-08-17T00:00:00Z"), "grade" : "A", "score" : 11 } ], "name" : "Regina Caterers", "restaurant_id" : "40356649" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd5"), "address" : { "building" : "1839", "loc" : { "type" : "Point", "coordinates" : [ -73.9482609, 40.6408271 ] }, "street" : "Nostrand Avenue", "zipcode" : "11226" }, "borough" : "Brooklyn", "cuisine" : "Ice Cream, Gelato, Yogurt, Ices", "grades" : [ { "date" : ISODate("2014-07-14T00:00:00Z"), "grade" : "A", "score" : 12 }, { "date" : ISODate("2013-07-10T00:00:00Z"), "grade" : "A", "score" : 8 }, { "date" : ISODate("2012-07-11T00:00:00Z"), "grade" : "A", "score" : 5 }, { "date" : ISODate("2012-02-23T00:00:00Z"), "grade" : "A", "score" : 8 } ], "name" : "Taste The Tropics Ice Cream", "restaurant_id" : "40356731" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd6"), "address" : { "building" : "7715", "loc" : { "type" : "Point", "coordinates" : [ -73.9973325, 40.61174889999999 ] }, "street" : "18 Avenue", "zipcode" : "11214" }, "borough" : "Brooklyn", "cuisine" : "American ", "grades" : [ { "date" : ISODate("2014-04-16T00:00:00Z"), "grade" : "A", "score" : 5 }, { "date" : ISODate("2013-04-23T00:00:00Z"), "grade" : "A", "score" : 2 }, { "date" : ISODate("2012-04-24T00:00:00Z"), "grade" : "A", "score" : 5 }, { "date" : ISODate("2011-12-16T00:00:00Z"), "grade" : "A", "score" : 2 } ], "name" : "C & C Catering Service", "restaurant_id" : "40357437" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd8"), "address" : { "building" : "1269", "loc" : { "type" : "Point", "coordinates" : [ -73.871194, 40.6730975 ] }, "street" : "Sutter Avenue", "zipcode" : "11208" }, "borough" : "Brooklyn", "cuisine" : "Chinese", "grades" : [ { "date" : ISODate("2014-09-16T00:00:00Z"), "grade" : "B", "score" : 21 }, { "date" : ISODate("2013-08-28T00:00:00Z"), "grade" : "A", "score" : 7 }, { "date" : ISODate("2013-04-02T00:00:00Z"), "grade" : "C", "score" : 56 }, { "date" : ISODate("2012-08-15T00:00:00Z"), "grade" : "B", "score" : 27 }, { "date" : ISODate("2012-03-28T00:00:00Z"), "grade" : "B", "score" : 27 } ], "name" : "May May Kitchen", "restaurant_id" : "40358429" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd9"), "address" : { "building" : "705", "loc" : { "type" : "Point", "coordinates" : [ -73.9653967, 40.6064339 ] }, "street" : "Kings Highway", "zipcode" : "11223" }, "borough" : "Brooklyn", "cuisine" : "Jewish/Kosher", "grades" : [ { "date" : ISODate("2014-11-10T00:00:00Z"), "grade" : "A", "score" : 11 }, { "date" : ISODate("2013-10-10T00:00:00Z"), "grade" : "A", "score" : 13 }, { "date" : ISODate("2012-10-04T00:00:00Z"), "grade" : "A", "score" : 7 }, { "date" : ISODate("2012-05-21T00:00:00Z"), "grade" : "A", "score" : 9 }, { "date" : ISODate("2011-12-30T00:00:00Z"), "grade" : "B", "score" : 19 } ], "name" : "Seuda Foods", "restaurant_id" : "40360045" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efdc"), "address" : { "building" : "203", "loc" : { "type" : "Point", "coordinates" : [ -73.97822040000001, 40.6435254 ] }, "street" : "Church Avenue", "zipcode" : "11218" }, "borough" : "Brooklyn", "cuisine" : "Ice Cream, Gelato, Yogurt, Ices", "grades" : [ { "date" : ISODate("2014-02-10T00:00:00Z"), "grade" : "A", "score" : 2 }, { "date" : ISODate("2013-01-02T00:00:00Z"), "grade" : "A", "score" : 13 }, { "date" : ISODate("2012-01-09T00:00:00Z"), "grade" : "A", "score" : 3 }, { "date" : ISODate("2011-11-07T00:00:00Z"), "grade" : "P", "score" : 12 }, { "date" : ISODate("2011-07-21T00:00:00Z"), "grade" : "A", "score" : 13 } ], "name" : "Carvel Ice Cream", "restaurant_id" : "40360076" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efdd"), "address" : { "building" : "6909", "loc" : { "type" : "Point", "coordinates" : [ -74.0259567, 40.6353674 ] }, "street" : "3 Avenue", "zipcode" : "11209" }, "borough" : "Brooklyn", "cuisine" : "Delicatessen", "grades" : [ { "date" : ISODate("2014-08-21T00:00:00Z"), "grade" : "A", "score" : 4 }, { "date" : ISODate("2014-03-05T00:00:00Z"), "grade" : "A", "score" : 3 }, { "date" : ISODate("2013-01-10T00:00:00Z"), "grade" : "A", "score" : 10 } ], "name" : "Nordic Delicacies", "restaurant_id" : "40361390" }
Type "it" for more
Dans un second temps, il faut voir que pour récupérer les différentes valeurs de notes, il faut acceder à chaque élément de la liste et non la liste entière. Pour y accéder, il faut utiliser la commande $unwind
, qui rendra donc à cette étape tous les restaurants de Brooklyn associés à une note qu’il a obtenu (Attention, cela retourne beaucoup de résultats : nombre de restaurants * nombre de notes)
db.NYfood.aggregate(
[
{$match: {"borough": "Brooklyn"}},
{$unwind: "$grades"},
]
)
{ "_id" : ObjectId("62309ad619b1d4e452b4efcc"), "address" : { "building" : "469", "loc" : { "type" : "Point", "coordinates" : [ -73.961704, 40.662942 ] }, "street" : "Flatbush Avenue", "zipcode" : "11225" }, "borough" : "Brooklyn", "cuisine" : "Hamburgers", "grades" : { "date" : ISODate("2014-12-30T00:00:00Z"), "grade" : "A", "score" : 8 }, "name" : "Wendy'S", "restaurant_id" : "30112340" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efcc"), "address" : { "building" : "469", "loc" : { "type" : "Point", "coordinates" : [ -73.961704, 40.662942 ] }, "street" : "Flatbush Avenue", "zipcode" : "11225" }, "borough" : "Brooklyn", "cuisine" : "Hamburgers", "grades" : { "date" : ISODate("2014-07-01T00:00:00Z"), "grade" : "B", "score" : 23 }, "name" : "Wendy'S", "restaurant_id" : "30112340" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efcc"), "address" : { "building" : "469", "loc" : { "type" : "Point", "coordinates" : [ -73.961704, 40.662942 ] }, "street" : "Flatbush Avenue", "zipcode" : "11225" }, "borough" : "Brooklyn", "cuisine" : "Hamburgers", "grades" : { "date" : ISODate("2013-04-30T00:00:00Z"), "grade" : "A", "score" : 12 }, "name" : "Wendy'S", "restaurant_id" : "30112340" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efcc"), "address" : { "building" : "469", "loc" : { "type" : "Point", "coordinates" : [ -73.961704, 40.662942 ] }, "street" : "Flatbush Avenue", "zipcode" : "11225" }, "borough" : "Brooklyn", "cuisine" : "Hamburgers", "grades" : { "date" : ISODate("2012-05-08T00:00:00Z"), "grade" : "A", "score" : 12 }, "name" : "Wendy'S", "restaurant_id" : "30112340" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efcf"), "address" : { "building" : "2780", "loc" : { "type" : "Point", "coordinates" : [ -73.98241999999999, 40.579505 ] }, "street" : "Stillwell Avenue", "zipcode" : "11224" }, "borough" : "Brooklyn", "cuisine" : "American ", "grades" : { "date" : ISODate("2014-06-10T00:00:00Z"), "grade" : "A", "score" : 5 }, "name" : "Riviera Caterer", "restaurant_id" : "40356018" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efcf"), "address" : { "building" : "2780", "loc" : { "type" : "Point", "coordinates" : [ -73.98241999999999, 40.579505 ] }, "street" : "Stillwell Avenue", "zipcode" : "11224" }, "borough" : "Brooklyn", "cuisine" : "American ", "grades" : { "date" : ISODate("2013-06-05T00:00:00Z"), "grade" : "A", "score" : 7 }, "name" : "Riviera Caterer", "restaurant_id" : "40356018" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efcf"), "address" : { "building" : "2780", "loc" : { "type" : "Point", "coordinates" : [ -73.98241999999999, 40.579505 ] }, "street" : "Stillwell Avenue", "zipcode" : "11224" }, "borough" : "Brooklyn", "cuisine" : "American ", "grades" : { "date" : ISODate("2012-04-13T00:00:00Z"), "grade" : "A", "score" : 12 }, "name" : "Riviera Caterer", "restaurant_id" : "40356018" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efcf"), "address" : { "building" : "2780", "loc" : { "type" : "Point", "coordinates" : [ -73.98241999999999, 40.579505 ] }, "street" : "Stillwell Avenue", "zipcode" : "11224" }, "borough" : "Brooklyn", "cuisine" : "American ", "grades" : { "date" : ISODate("2011-10-12T00:00:00Z"), "grade" : "A", "score" : 12 }, "name" : "Riviera Caterer", "restaurant_id" : "40356018" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd3"), "address" : { "building" : "7114", "loc" : { "type" : "Point", "coordinates" : [ -73.9068506, 40.6199034 ] }, "street" : "Avenue U", "zipcode" : "11234" }, "borough" : "Brooklyn", "cuisine" : "Delicatessen", "grades" : { "date" : ISODate("2014-05-29T00:00:00Z"), "grade" : "A", "score" : 10 }, "name" : "Wilken'S Fine Food", "restaurant_id" : "40356483" }
{ "_id" : ObjectId("62309ad619b1d4e452b4efd3"), "address" : { "building" : "7114", "loc" : { "type" : "Point", "coordinates" : [ -73.9068506, 40.6199034 ] }, "street" : "Avenue U", "zipcode" : "11234" }, "borough" : "Brooklyn", "cuisine" : "Delicatessen", "grades" : { "date" : ISODate("2014-01-14T00:00:00Z"), "grade" : "A", "score" : 10 }, "name" : "Wilken'S Fine Food", "restaurant_id" : "40356483" }
Type "it" for more
Ensuite, pour savoir quelle note a été la plus attribuée, il faut grouper pour chaque valeur de note. On utilise donc un $group
sur l’attribut $grades.grade$ (accessible grâce a
$unwind). Puis, on décide de compter le nombre d'itérations de chaque note stockée dans la variable
nb`. Cette étape nous retourne donc le nombre de fois où chaque note a été attribuée à un restaurant.
db.NYfood.aggregate(
[
{$match: {"borough": "Brooklyn"}},
{$unwind: "$grades"},
{$group: {_id: "$grades.grade", nb: {$sum: 1}}},
]
)
{ "_id" : "A", "nb" : 17324 }
{ "_id" : "B", "nb" : 3055 }
{ "_id" : "P", "nb" : 343 }
{ "_id" : "Z", "nb" : 285 }
{ "_id" : "Not Yet Graded", "nb" : 144 }
{ "_id" : "C", "nb" : 811 }
Nous sommes donc tout proches du résultat espéré. Il reste maintenant à trier les résultats par ordre décroissant, afin d’avoir les notes les plus données au début : {$sort: {nb: -1}}
. Mais comme l’énoncé le précise, on souhaite afficher uniquement les 3 notes les plus données. Etant donné que les notes sont triées, il faut seulement préciser : {$limit: 3}
.
db.NYfood.aggregate(
[
{$match: {"borough": "Brooklyn"}},
{$unwind: "$grades"},
{$group: {_id: "$grades.grade", nb: {$sum: 1}}},
{$sort: {nb: -1}},
{$limit: 3}
]
)
{ "_id" : "A", "nb" : 17324 }
{ "_id" : "B", "nb" : 3055 }
{ "_id" : "C", "nb" : 811 }
On obtient bien, avec cette requête, les 3 notes les plus attribuées aux restaurants de Brooklyn !
db.NYfood.aggregate(
[
{$project: {taille: {$size: "$grades"}}},
{$match :{taille:{$gt:2}}},
{$group: {_id: null,
nb_min: {$min: "$taille"},
nb_max: {$max: "$taille"}}
},
]
)
{ "_id" : null, "nb_min" : 3, "nb_max" : 9 }
Dans cette deuxième requête, on montre bien ici qu’il n’y a pas d’ordre pré-défini d’étape, et ici le $match
n’est ni au début de la requête, ni à la fin.
Expliquons cette requête (qui n’a pas beaucoup d’intérêt pratique).
$project
: création de la variable taille, qui correspond au nombre de notes données à un restaurant.$match
: Dans le tableau rendu précédemment, on ne prend que les restaurants ayant plus de 2 notes.$group
: Sur le résultat de la requête précédente, on groupe tout les restaurants (_id : null), et on regarde le nombre minimum et maximum de notes attribuées à un restaurant. (Ayant sélectionné les individus supérieurs à deux, le minimum ne pouvait être que 3 ou plus.
En SQL, on aurait :
SELECT COUNT(*) AS taille, MAX(taille),MIN(taille)
FROM NYfood
WHERE taille>=2
Résultat final : Le nombre minimum et maximum de notes attribué aux restaurants ayant au moins deux notes.