HOWTO · MongoDB
Utiliser un pipeline dans l’opérateur `$lookup` de MongoDB pour joindre des documents
Utilisez un pipeline MongoDB `$lookup` pour joindre des documents selon `shopId`, exiger l’absence de `shopPosId`, puis filtrer éventuellement les résultats sans correspondance.
Sur cette page
Utilisez un pipeline dans l’étape $lookup de MongoDB pour joindre les documents de collection2 à ceux de collection1 lorsque leurs valeurs shopId correspondent et que le document de collection1 ne possède pas de champ shopPosId. $lookup réalise une jointure externe gauche : tous les documents d’entrée de collection2 restent dans le résultat, et une ligne sans correspondance contient un tableau shopDescription vide. Ajoutez une étape $match ultérieure si vous souhaitez conserver uniquement les lignes auxquelles un document a été joint.
Qu’est-ce qu’un pipeline d’agrégation
Un pipeline d’agrégation traite les documents au moyen d’étapes exécutées dans un ordre précis. Chaque étape peut filtrer ou remanier les documents qu’elle reçoit, puis transmet son résultat à l’étape suivante. Dans cet exemple, $lookup ajoute les documents correspondants et une étape $match suivante peut filtrer le résultat de la jointure. MongoDB propose également map-reduce et des opérations d’agrégation à usage unique, mais elles ne sont pas nécessaires pour cette jointure.
Pour en savoir plus, consultez la présentation du pipeline d’agrégation de MongoDB.
Qu’est-ce que l’opérateur $lookup dans MongoDB
$lookup joint des documents provenant d’une autre collection de la même base de données et ajoute les correspondances dans un champ de type tableau. L’option from indique la collection à consulter, tandis que as donne le nom du tableau ajouté à chaque document d’entrée. La forme avec pipeline permet d’appliquer des conditions aux documents à joindre.
La référence MongoDB de $lookup présente les syntaxes disponibles. L’exemple ci-dessous utilise un pipeline corrélé : let transmet une valeur du document d’entrée au pipeline, et $expr permet à $match de comparer des champs à cette valeur. Les commandes du shell MongoDB de cet article sont des exemples à exécuter sur une base de données MongoDB ; l’environnement d’exécution configuré pour l’article n’effectue pas d’opérations sur une base de données.
Utiliser un pipeline dans $lookup pour effectuer une jointure conditionnelle dans MongoDB
Créez collection1 et collection2 avec les documents suivants. La configuration est présentée sous forme d’exemple pour le shell MongoDB ; exécutez chaque instruction dans mongosh, sur la base de données de votre choix.
db.createCollection('collection1'); db.createCollection('collection2'); db.collection1.insertMany([{"shopId":"001","shopPosId":"001","description":"description for 001"},{"shopId":"002","description":"description for 002"},{"shopId":"003","shopPosId":"003","description":"description for 003"},{"shopId":"004","description":"description for 004"}]); db.collection2.insertMany([{"shopId":"001","shopPosId":"0078","clientUid":"474192"},{"shopId":"002","shopPosId":"0012","clientUid":"474193"},{"shopId":"003","shopPosId":"0034","clientUid":"474194"},{"shopId":"004","shopPosId":"0056","clientUid":"474195"}]);
L’exemple Python autonome suivant modélise la même correspondance avec les données fournies : les boutiques 002 et 004 reçoivent des descriptions jointes, car leurs documents dans collection1 ont une valeur shopId correspondante et ne possèdent pas shopPosId. Il illustre la logique du résultat ; ce n’est pas une requête MongoDB.
Exécutez cet exemple :
collection1 = [
{"shopId": "001", "shopPosId": "001", "description": "description for 001"},
{"shopId": "002", "description": "description for 002"},
{"shopId": "003", "shopPosId": "003", "description": "description for 003"},
{"shopId": "004", "description": "description for 004"},
]
collection2 = [
{"shopId": "001", "shopPosId": "0078", "clientUid": "474192"},
{"shopId": "002", "shopPosId": "0012", "clientUid": "474193"},
{"shopId": "003", "shopPosId": "0034", "clientUid": "474194"},
{"shopId": "004", "shopPosId": "0056", "clientUid": "474195"},
]
for source in collection2:
matches = [
item["description"]
for item in collection1
if item["shopId"] == source["shopId"] and "shopPosId" not in item
]
print(f'{source["shopId"]}: {matches}')
001: []
002: ['description for 002']
003: []
004: ['description for 004']
Dans le pipeline MongoDB, définissez let: {shopId: "$shopId"} pour transmettre l’identifiant de chaque ligne de collection2 au pipeline de recherche. Effectuez la correspondance avec $expr: {$eq: ["$shopId", "$$shopId"]} ; le préfixe $$ désigne la variable définie par let. Ajoutez cette comparaison à {shopPosId: {$exists: false}} dans $and afin d’exiger l’absence du champ dans le document joint de collection1.
La première requête correspondante est db.collection2.aggregate([{$lookup: {from: "collection1", let: {shopId: "$shopId"}, pipeline: [{$match: {$and: [{$expr: {$eq: ["$shopId", "$$shopId"]}}, {shopPosId: {$exists: false}}]}}], as: "shopDescription"}}]). Elle conserve les quatre documents d’entrée de collection2. Les boutiques 001 et 003 n’ont aucun document correspondant dans collection1 qui satisfasse les conditions ; leurs tableaux shopDescription sont donc vides. Les boutiques 002 et 004 correspondent, car leurs documents joints ne possèdent pas shopPosId.
Pour ne renvoyer que les lignes ayant une correspondance, ajoutez une étape $match qui vérifie que le tableau joint contient au moins un élément : {$match: {shopDescription: {$ne: []}}}. La requête complète qui suit est db.collection2.aggregate([{$lookup: {from: "collection1", let: {shopId: "$shopId"}, pipeline: [{$match: {$and: [{$expr: {$eq: ["$shopId", "$$shopId"]}}, {shopPosId: {$exists: false}}]}}], as: "shopDescription"}}, {$match: {shopDescription: {$ne: []}}}]). Avec les données fournies, elle renvoie les lignes sources des boutiques 002 et 004. Les libellés affichés dans l’exemple restent en anglais. La première étape $lookup demeure une jointure externe gauche ; la seconde étape supprime les lignes dont le tableau joint est vide.