MongoDB Academy · 课时

使用官方 Node.js 驱动程序连接

您将创建 MongoClient,管理连接池,并在 Node.js 应用中使用原生驱动程序执行 CRUD 操作。

第 1 / 4 课13 个步骤

使用官方 Node.js 驱动程序连接 是 CoddyKit 上的免费 MongoDB Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 MongoDB Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 MongoDB Academy 课程共包含 4 节课。

官方 MongoDB Node.js 驱动程序

MongoDB Node.js 驱动程序(mongodb npm 软件包)是用于从 Node.js 应用程序连接 MongoDB 的官方底层库。它无需抽象层即可直接访问 MongoDB 的所有功能,因此非常适合对性能要求较高的代码、微服务和脚本。该驱动程序由 MongoDB 公司维护,并且会紧密跟进 MongoDB 服务器的新功能。

// Install the driver
// npm install mongodb

// Or with yarn:
// yarn add mongodb

// The driver exports MongoClient as its main entry point
const { MongoClient, ObjectId, ServerApiVersion } = require('mongodb');
// or ES module:
// import { MongoClient, ObjectId } from 'mongodb';

创建 MongoClient

MongoClient 类是所有交互的入口点。请使用您的连接字符串(URI)以及可选的选项对象对其实例化。连接字符串会编码主机、端口、凭据和连接参数。对于 MongoDB Atlas,请从 Atlas 界面复制连接字符串,并替换密码占位符。每个应用程序只创建一个 MongoClient 并重复使用,不要为每个请求创建新的 MongoClient。

const { MongoClient, ServerApiVersion } = require('mongodb');

const uri = process.env.MONGODB_URI;
// URI format: mongodb+srv://<user>:<password>@cluster0.xxxxx.mongodb.net/?retryWrites=true

const client = new MongoClient(uri, {
  serverApi: {
    version: ServerApiVersion.v1,
    strict: true,
    deprecationErrors: true
  }
});

// client is not yet connected — connecting happens lazily or via connect()

连接到 MongoDB

调用 client.connect() 来建立连接池。该驱动程序会维护一个连接池,通常包含 5 到 100 个与服务器的连接,并在各项操作之间重复使用这些连接。您只需在应用程序启动时调用一次 connect()。您也可以直接调用操作而不显式调用 connect()——驱动程序会在第一次操作时延迟建立连接。请在应用程序关闭时调用 client.close()。

async function main() {
  try {
    await client.connect();
    console.log('Connected to MongoDB');

    // Get database reference
    const db = client.db('myDatabase');
    
    // Ping the server to verify connection
    await db.command({ ping: 1 });
    console.log('Ping successful');

    // Run your application logic here...
  } finally {
    // Always close when done
    await client.close();
  }
}

main().catch(console.error);

Web 服务器的单例模式

在 Web 服务器(Express、Fastify、NestJS)中,MongoClient 必须是一个模块级单例,在启动时创建一次,并由所有请求处理程序共享。为每个请求创建新的 MongoClient 会耗尽可用连接,并严重降低性能。标准模式是在服务器启动函数中建立连接,然后导出客户端或数据库引用。

// db.js — module-level singleton
const { MongoClient } = require('mongodb');

let client;
let db;

async function connectToDatabase() {
  if (db) return db; // return existing connection
  client = new MongoClient(process.env.MONGODB_URI);
  await client.connect();
  db = client.db(process.env.DB_NAME || 'myapp');
  return db;
}

module.exports = { connectToDatabase };

// In your Express app:
// const { connectToDatabase } = require('./db');
// const db = await connectToDatabase();
// app.locals.db = db;

获取集合引用

通过调用 db.collection('collectionName') 访问集合。此调用不会发起网络请求,而是返回一个Collection 对象,也就是一个引用。您可以在启动时获取集合引用并将其存储为模块级变量,也可以在每个函数中内联获取。集合引用非常轻量,可以安全地在各个请求之间重复使用。

const db = client.db('ecommerce');

// Get collection references
const usersCollection = db.collection('users');
const ordersCollection = db.collection('orders');
const productsCollection = db.collection('products');

// TypeScript: provide a document type for type safety
// const users = db.collection<UserDocument>('users');

// Collections can also be retrieved inline:
async function getUser(id) {
  return client.db('ecommerce').collection('users').findOne({ _id: id });
}

增删改查操作:插入

使用 insertOne() 或 insertMany() 插入文档。如果未提供 _id,驱动程序会自动生成,并返回插入的 ID。结果对象中的 insertedId(用于 insertOne)或 insertedIds 映射(用于 insertMany)可以帮助您追踪创建的内容。这两种操作都接受一个 options 对象,您可以在其中指定 writeConcern。

const db = client.db('shop');
const products = db.collection('products');

// Insert one document
const insertResult = await products.insertOne({
  name: 'Mechanical Keyboard',
  price: 149.99,
  category: 'Electronics',
  stock: 50
});
console.log('Inserted ID:', insertResult.insertedId);

// Insert multiple documents
const bulkResult = await products.insertMany([
  { name: 'Mouse Pad', price: 19.99, category: 'Accessories' },
  { name: 'USB Hub', price: 39.99, category: 'Electronics' }
]);
console.log('Inserted count:', bulkResult.insertedCount);

增删改查操作:读取

使用 findOne(filter) 获取单个文档,或使用 find(filter) 获取一个遍历所有匹配文档的游标。对于较小的结果集,可以使用 .toArray() 将游标转换为数组;对于较大的结果集,可以使用 for await...of 进行迭代。这两种方法都接受第二个选项参数,可用于指定 projection、sort、skip、limit 和其他查询修饰符。

const users = db.collection('users');

// Find a single user by email
const user = await users.findOne(
  { email: 'alice@example.com' },
  { projection: { password: 0 } }  // exclude sensitive fields
);

// Find multiple documents with options
const activeUsers = await users.find(
  { active: true, role: 'admin' },
  {
    projection: { name: 1, email: 1 },
    sort: { createdAt: -1 },
    limit: 50
  }
).toArray();

// Iterate large results without loading all into memory
const cursor = users.find({ active: true });
for await (const user of cursor) {
  await processUser(user);
}

增删改查操作:更新

使用 updateOne()、updateMany() 或 findOneAndUpdate() 更新文档。筛选条件决定要更新哪些文档,更新文档则使用 $set、$inc 和 $push 等运算符指定更改内容。updateOne() 和 updateMany() 会返回包含 matchedCount 和 modifiedCount 的结果。使用 upsert: true 可以在文档不存在时创建它。

const orders = db.collection('orders');

// Update one order's status
const updateResult = await orders.updateOne(
  { _id: orderId },
  { $set: { status: 'shipped', shippedAt: new Date() } }
);
console.log('Modified:', updateResult.modifiedCount);

// Atomic: find, update, and return the updated document
const updatedOrder = await orders.findOneAndUpdate(
  { _id: orderId },
  { $set: { status: 'delivered' }, $push: { statusHistory: { status: 'delivered', at: new Date() } } },
  { returnDocument: 'after' }  // return the document AFTER update
);

增删改查操作:删除

使用 deleteOne() 或 deleteMany() 删除文档。筛选条件决定要删除哪些文档。结果中包含 deletedCount。为确保生产环境安全,在运行 deleteMany 之前,请始终先使用 find 测试筛选条件——意外删除集合中的所有文档是一种常见且灾难性的错误。对于审计追踪,可以考虑使用软删除(设置 deletedAt 字段),而不是执行物理删除。

const sessions = db.collection('sessions');

// Delete one session
const deleteResult = await sessions.deleteOne({ _id: sessionId });
console.log('Deleted:', deleteResult.deletedCount);

// Delete all expired sessions — test filter first!
const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);

// 1. Test: how many would be deleted?
const count = await sessions.countDocuments({ expiresAt: { $lt: thirtyDaysAgo } });
console.log('Would delete:', count);

// 2. Only delete after confirming the count looks right
if (count < 10000) { // sanity check
  await sessions.deleteMany({ expiresAt: { $lt: thirtyDaysAgo } });
}

运行聚合管道

使用 collection.aggregate(pipeline) 执行聚合管道。该调用会返回一个游标,您可以将其转换为数组或进行迭代。管道阶段以对象数组的形式传入。驱动程序会将管道发送到 MongoDB 的聚合引擎,并以流式方式返回结果。对于非常大的结果集,请直接迭代游标,而不要调用 .toArray(),以避免一次性将所有内容加载到内存中。

const orders = db.collection('orders');

// Revenue report by category
const report = await orders.aggregate([
  { $match: { status: 'completed', createdAt: { $gte: new Date('2024-01-01') } } },
  { $group: { _id: '$category', total: { $sum: '$amount' }, count: { $sum: 1 } } },
  { $sort: { total: -1 } }
]).toArray();

console.log('Revenue report:', report);

// For large aggregations, iterate the cursor:
const cursor = orders.aggregate([...largeComplexPipeline]);
for await (const doc of cursor) {
  await writeToReport(doc);
}

连接池配置

MongoClient 会维护一个连接池,自动管理与服务器的连接。关键选项包括:maxPoolSize(最大连接数,每个主机默认为 5)、minPoolSize(保持活动状态的最小连接数)、connectTimeoutMS 和 socketTimeoutMS。对于高流量 API,请增大 maxPoolSize,但要权衡 MongoDB 每个连接的内存开销。请使用 Atlas 指标监控连接池的使用情况。

const client = new MongoClient(uri, {
  maxPoolSize: 20,           // max 20 connections to the server
  minPoolSize: 5,            // keep at least 5 connections warm
  connectTimeoutMS: 5000,   // fail fast if can't connect in 5s
  socketTimeoutMS: 45000,   // idle socket timeout
  serverSelectionTimeoutMS: 5000  // how long to wait to find an available server
});

快速检查

请测试您对本课 MongoDB 和 NoSQL 数据库概念的理解。

课程回顾

本课您学到了:在启动时创建一个 MongoClient,并以单例形式重复使用——绝不要为每个请求创建一个;使用 client.db() 和 db.collection() 获取数据库和集合引用——这些都是轻量级的对象引用;以及使用 insertOne/insertMany、findOne/find、updateOne/updateMany/findOneAndUpdate 和 deleteOne/deleteMany 执行增删改查操作。接下来,我们将探索 Mongoose 的 Schema、Model 和虚拟字段,了解这种基于原生驱动程序的更高层抽象。

免费开始

用 AI 导师学习 JavaScript — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
30
课程
120

常见问题解答

「使用官方 Node.js 驱动程序连接」课时是免费的吗?

是的 — 「使用官方 Node.js 驱动程序连接」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 MongoDB Academy 课程的其余内容,请升级到 CoddyKit PRO。 MongoDB Academy 课程共包含 4 节课。

「使用官方 Node.js 驱动程序连接」这节课中我会学到什么?

您将创建 MongoClient,管理连接池,并在 Node.js 应用中使用原生驱动程序执行 CRUD 操作。 你通过在浏览器中直接运行的动手代码来练习 MongoDB Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 MongoDB Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 MongoDB Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。

「使用官方 Node.js 驱动程序连接」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 MongoDB Academy 课中编写并运行代码吗?

能。每节 MongoDB Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 使用官方 Node.js 驱动程序连接
  2. Mongoose 模式、模型和虚拟属性
  3. Mongoose 查询、链式调用和精简文档
  4. Mongoose 中间件:前置和后置钩子
← 返回 MongoDB Academy