bhylu.com

Advertisements

Node.js MongoDB Lookup

An aggregation pipeline can join a collection with related documents through $lookup. Decide whether the result should contain one parent with an array or one row per relationship.

How it works

The fixture creates a single loan and joins its kit by the unique _id field. $lookup returns an array, so the result shape must be interpreted before reporting a quantity. If several matches are possible, unwinding or summing can change row grain and multiply values. MongoDB does not create a foreign-key constraint automatically from this lookup; referential validity remains part of the application or schema policy.

Run the code

Run this example locally with Node.js. The exact sources are reviewed on Node 22.15.1 and Node 24.22.1 unless a specialised lab states otherwise. Save the complete source as mongo-lookup.mjs in a separate empty exercises folder and run node mongo-lookup.mjs. There is no Node.js browser runner on Bhylu; the plain JavaScript playground does not provide these filesystem, server or process APIs. In that separate project, install the reviewed packages with npm install --save-exact mongodb@6.17.0. These are teaching dependencies, not changes to the Bhylu website package tree. This database example is reviewed on Node 22.15.1 against its named real database lab. Do not run it against an existing application database. Read the dedicated lab requirements below before execution.

Download the complete source. Ordinary Node scripts need no Apache, PHP or MySQL service. File examples own their temporary folders; network fixtures use loopback and available temporary ports.

Dedicated MongoDB lab

Use MongoDB Community 8.0.12 and mongodb driver 6.17.0 in a disposable authenticated loopback instance. The script requires BHYLU_NODE_DB_LAB=YES and BHYLU_MONGO_URI, including the lab host, port, authentication database and your private generated credentials. It creates a random bhylu_node_ database, inserts fictional records and drops only that database in finally. Do not use an Atlas or existing application URI. The review is a standalone instance, not a replica-set transaction, sharding or failover test.

Example

mongo-lookup.mjs

import { MongoClient } from 'mongodb';
import { randomBytes } from 'node:crypto';
const uri = process.env.BHYLU_MONGO_URI;
let endpoint;
try {
  endpoint = new URL(uri);
} catch {
  throw new Error('Use the dedicated loopback MongoDB lab');
}
if (
  process.env.BHYLU_NODE_DB_LAB !== 'YES' ||
  endpoint.protocol !== 'mongodb:' ||
  endpoint.hostname !== '127.0.0.1' ||
  !endpoint.port ||
  endpoint.port === '27017'
)
  throw new Error('Use the dedicated loopback MongoDB lab');
const client = new MongoClient(uri, { serverSelectionTimeoutMS: 5000 });
await client.connect();
const database = client.db('bhylu_node_' + randomBytes(8).toString('hex'));
try {
  const kits = database.collection('kits');
  await kits.insertMany([
    { _id: 701, name: 'tripod', available: 3 },
    { _id: 702, name: 'binoculars', available: 2 },
    { _id: 703, name: 'torch', available: 0 },
  ]);
  await database.collection('loans').insertOne({ _id: 801, kitId: 701, quantity: 1 });
  const rows = await database
    .collection('loans')
    .aggregate([
      {
        $lookup: { from: 'kits', localField: 'kitId', foreignField: '_id', as: 'kit' },
      },
      { $sort: { _id: 1 } },
    ])
    .toArray();
  console.log(rows.length, rows[0]._id, rows[0].kit.length, rows[0].kit[0].name);
} finally {
  try {
    await database.dropDatabase();
  } finally {
    await client.close();
  }
}

Expected result

1 801 1 tripod

The one loan has one matching kit in an array. That explicit interpretation is more useful than treating every aggregation output as interchangeable tabular rows. A missing referenced kit would produce a different relationship result and should have a tested policy.

Use the Node.js API reference for the precise version contract.

Advertisements