One-to-One Join
On this page
Introduction
In this tutorial, you can learn how to use the Node.js driver to construct an aggregation pipeline, perform the aggregation on a collection, and print the results by completing and running a sample app.
This aggregation performs a one-to-one join. A one-to-one join occurs when a document in one collection has a field value that matches a single document in another collection that has the same field value. The aggregation matches these documents on the field value and combines information from both sources into one result.
Tip
A one-to-one join does not require the documents to have a one-to-one relationship. To learn more about this data relationship, see the Wikipedia entry about One-to-one (data model).
Aggregation Task Summary
This tutorial demonstrates how to combine data from a collection that describes product information with another collection that describes customer orders. The results show a list of all orders placed in 2020 that includes the product details associated with each order.
This example uses two collections:
- orders: contains documents describing individual orders for products in a shop
- products: contains documents describing the products that a shop sells
An order can only contain one product, so the aggregation uses a
one-to-one join to match an order document to the document for the
product. The collections are joined by a field called product_id
that exists in documents in both collections.
Before You Get Started
Before you start this tutorial, complete the Aggregation Template App instructions to set up a working Node.js application.
After you set up the app, access the orders and products
collections by adding the following code to the application:
const ordersColl = aggDB.collection("orders"); const productsColl = aggDB.collection("products"); 
Delete any existing data and insert sample data into
the orders collection as shown in the following code:
await ordersColl.deleteMany({}); const orderData = [   {     customer_id: "elise_smith@myemail.com",     orderdate: new Date("2020-05-30T08:35:52Z"),     product_id: "a1b2c3d4",     value: 431.43,   },   {     customer_id: "tj@wheresmyemail.com",     orderdate: new Date("2019-05-28T19:13:32Z"),     product_id: "z9y8x7w6",     value: 5.01,   },   {     customer_id: "oranieri@warmmail.com",     orderdate: new Date("2020-01-01T08:25:37Z"),     product_id: "ff11gg22hh33",     value: 63.13,   },   {     customer_id: "jjones@tepidmail.com",     orderdate: new Date("2020-12-26T08:55:46Z"),     product_id: "a1b2c3d4",     value: 429.65,   }, ]; await ordersColl.insertMany(orderData); 
Delete any existing data and insert sample data into
the products collection as shown in the following code:
await productsColl.deleteMany({}); const productData = [   {     id: "a1b2c3d4",     name: "Asus Laptop",     category: "ELECTRONICS",     description: "Good value laptop for students",   },   {     id: "z9y8x7w6",     name: "The Day Of The Triffids",     category: "BOOKS",     description: "Classic post-apocalyptic novel",   },   {     id: "ff11gg22hh33",     name: "Morphy Richardds Food Mixer",     category: "KITCHENWARE",     description: "Luxury mixer turning good cakes into great",   },   {     id: "pqr678st",     name: "Karcher Hose Set",     category: "GARDEN",     description: "Hose + nosels + winder for tidy storage",   }, ]; await productsColl.insertMany(productData); 
Tutorial
Add a match stage for orders in 2020
Add a $match stage that matches orders placed in 2020:
pipeline.push({   $match: {     orderdate: {       $gte: new Date("2020-01-01T00:00:00Z"),       $lt: new Date("2021-01-01T00:00:00Z"),     },   }, }); 
Add a lookup stage to link the collections
Next, add a $lookup stage. The
$lookup stage joins the product_id field in the orders
collection to the id field in the products collection:
pipeline.push({   $lookup: {     from: "products",     localField: "product_id",     foreignField: "id",     as: "product_mapping",   }, }); 
Add set stages to create new document fields
Next, add two $set stages to the pipeline.
The first $set stage sets the product_mapping field
to the first element in the product_mapping object
created in the previous $lookup stage.
The second $set stage creates two new fields, product_name
and product_category, from the values in the
product_mapping object field:
pipeline.push(     {       $set: {         product_mapping: { $first: "$product_mapping" },       },     },     {       $set: {         product_name: "$product_mapping.name",         product_category: "$product_mapping.category",       },     }   ); 
Tip
Because this is a one-to-one join, the $lookup stage
adds only one array element to the input document. The pipeline
uses the $first
operator to retrieve the data from this element.
Add an unset stage to remove unneeded fields
Finally, add an $unset stage. The
$unset stage removes unnecessary fields from the document:
pipeline.push({ $unset: ["_id", "product_id", "product_mapping"] }); 
Interpret results
The aggregated result contains three documents. The documents
represent customer orders that occurred in 2020, with the
product_name and product_category of the ordered product:
{   customer_id: 'elise_smith@myemail.com',   orderdate: 2020-05-30T08:35:52.000Z,   value: 431.43,   product_name: 'Asus Laptop',   product_category: 'ELECTRONICS' } {   customer_id: 'oranieri@warmmail.com',   orderdate: 2020-01-01T08:25:37.000Z,   value: 63.13,   product_name: 'Morphy Richardds Food Mixer',   product_category: 'KITCHENWARE' } {   customer_id: 'jjones@tepidmail.com',   orderdate: 2020-12-26T08:55:46.000Z,   value: 429.65,   product_name: 'Asus Laptop',   product_category: 'ELECTRONICS' } 
The result consists of documents that contain fields from
documents in the orders collection and the products
collection, joined by matching the product_id field present in
each original document.
To view the complete code for this tutorial, see the Completed One-to-one Join App on GitHub.