在 Mongoose 中实现 deep populate 的完整代码

介绍

Mongoose 是一个流行的 Node.js 的 ORM(对象关系映射)框架。它提供了非常方便的方式去定义和操作 MongoDB 的数据文档,支持查询,更新,删除等操作。其中, populate 功能可以实现深度获取数据库数据的相关联数据。本文将介绍如何使用 Mongoose 实现深度 populate,包含完整代码和详细讲解。

准备工作

在开始使用 Mongoose 的深度 populate 功能之前,需要先设置 MongoDB 连接。如果你还没有安装 MongoDB,可以访问 MongoDB 官网 安装。在安装好 MongoDB 之后,你需要在本地启动 MongoDB 服务,并创建一个数据库和一个用户。

在启动 MongoDB 后,你需要在命令行中输入以下命令创建一个数据库和一个用户:

这里我们创建了一个名为 test 的数据库,并为 test 用户赋予了读写权限。

接下来,你需要安装 Mongoose 和 lodash 模块。你可以使用 npm 安装这两个模块。

创建数据模型

为了实现深度 populate,我们需要定义几个数据模型。这里我们定义了 Book、Author、Publisher 三个模型,它们之间存在多对多和一对多的关系。

  • Book 模型:
const bookSchema = new mongoose.Schema({
  title: { type: String, required: true },
  author: { type: mongoose.Schema.Types.ObjectId, ref: 'Author' },
  publishers: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Publisher' }],
});

const Book = mongoose.model('Book', bookSchema);
  • Author 模型:
const authorSchema = new mongoose.Schema({
  name: { type: String, required: true },
  books: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Book' }],
});

const Author = mongoose.model('Author', authorSchema);
  • Publisher 模型:
const publisherSchema = new mongoose.Schema({
  name: { type: String, required: true },
  books: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Book' }],
});

const Publisher = mongoose.model('Publisher', publisherSchema);

这里只定义了模型的 schema,我们还需要在代码中连接到 MongoDB 并使用模型。完整的代码如下:

const mongoose = require('mongoose');
const _ = require('lodash');

// 连接到 MongoDB
mongoose.connect('mongodb://test:123456@localhost:27017/test', {
  useNewUrlParser: true,
  useUnifiedTopology: true,
});

// 定义 Book 模型
const bookSchema = new mongoose.Schema({
  title: { type: String, required: true },
  author: { type: mongoose.Schema.Types.ObjectId, ref: 'Author' },
  publishers: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Publisher' }],
});

const Book = mongoose.model('Book', bookSchema);

// 定义 Author 模型
const authorSchema = new mongoose.Schema({
  name: { type: String, required: true },
  books: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Book' }],
});

const Author = mongoose.model('Author', authorSchema);

// 定义 Publisher 模型
const publisherSchema = new mongoose.Schema({
  name: { type: String, required: true },
  books: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Book' }],
});

const Publisher = mongoose.model('Publisher', publisherSchema);

创建数据

我们需要先创建一些数据,以便进行深度 populate 的实验。这里我们创建两个作者,两个出版社,和两本书,它们之间存在多对多和一对多的关系。

// 创建作者和出版社
const author1 = await Author.create({ name: 'Tom' });
const author2 = await Author.create({ name: 'Jerry' });

const publisher1 = await Publisher.create({ name: 'Penguin' });
const publisher2 = await Publisher.create({ name: 'Oxford' });

// 创建书籍
const book1 = await Book.create({
  title: 'Book1',
  author: author1._id,
  publishers: [publisher1._id, publisher2._id],
});

const book2 = await Book.create({
  title: 'Book2',
  author: author2._id,
  publishers: [publisher1._id],
});

// 更新作者和出版社的书籍列表
await Author.updateOne({ _id: author1._id }, { $push: { books: book1._id } });
await Author.updateOne({ _id: author2._id }, { $push: { books: book2._id } });

await Publisher.updateOne(
  { _id: publisher1._id },
  { $push: { books: book1._id, books: book2._id } }
);

await Publisher.updateOne({ _id: publisher2._id }, { $push: { books: book1._id } });

这段代码先创建了两个作者和两个出版社,然后创建了两本书,并将它们关联到相应的模型中。

注意,当创建多对多关系时,需要使用数组类型。在这个例子中,我们使用了以下代码来将书籍与出版社相关联。

await Publisher.updateOne(
  { _id: publisher1._id },
  { $push: { books: book1._id, books: book2._id } }
);

这里我们在 books 数组中添加了两个 ObjectId,分别对应 book1 和 book2 的 id。

当创建完数据后,我们就可以进行深度 populate 的实验了。

深度 populate 实现

深度 populate 可以让我们在查询 Mongoose 数据库时,一次性获取多层嵌套的相关联数据。在使用 populate 函数获取数据时,可以设置 options 来实现深度 populate。

const options = {
  path: 'author',
  populate: {
    path: 'books',
    populate: {
      path: 'publishers',
    },
  },
};

在 options 中的每个 path 属性都表示一个需要 populate 的字段。这里我们定义了深度为 2 的 path 链,分别对应 Book 模型中的 author、Publisher 模型中的 books 和 Book 模型中的 publishers。

接下来,我们使用 Mongoose 提供的 populate 方法获取数据。

const books = await Book.find().populate(options);
console.log(JSON.stringify(books, null, 2));

这里我们获取了 Book 模型下的所有数据,并使用 options 参数采用深度 populate 的方式获取相关联数据。调用 populate 方法获取数据后,我们使用 JSON.stringify 方法将其输出到控制台。

完整代码如下:

const mongoose = require('mongoose');
const _ = require('lodash');

// 连接到 MongoDB
mongoose.connect('mongodb://test:123456@localhost:27017/test', {
  useNewUrlParser: true,
  useUnifiedTopology: true,
});

// 定义 Book 模型
const bookSchema = new mongoose.Schema({
  title: { type: String, required: true },
  author: { type: mongoose.Schema.Types.ObjectId, ref: 'Author' },
  publishers: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Publisher' }],
});

const Book = mongoose.model('Book', bookSchema);

// 定义 Author 模型
const authorSchema = new mongoose.Schema({
  name: { type: String, required: true },
  books: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Book' }],
});

const Author = mongoose.model('Author', authorSchema);

// 定义 Publisher 模型
const publisherSchema = new mongoose.Schema({
  name: { type: String, required: true },
  books: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Book' }],
});

const Publisher = mongoose.model('Publisher', publisherSchema);

(async () => {
  // 创建作者和出版社
  const author1 = await Author.create({ name: 'Tom' });
  const author2 = await Author.create({ name: 'Jerry' });

  const publisher1 = await Publisher.create({ name: 'Penguin' });
  const publisher2 = await Publisher.create({ name: 'Oxford' });

  // 创建书籍
  const book1 = await Book.create({
    title: 'Book1',
    author: author1._id,
    publishers: [publisher1._id, publisher2._id],
  });

  const book2 = await Book.create({
    title: 'Book2',
    author: author2._id,
    publishers: [publisher1._id],
  });

  // 更新作者和出版社的书籍列表
  await Author.updateOne({ _id: author1._id }, { $push: { books: book1._id } });
  await Author.updateOne({ _id: author2._id }, { $push: { books: book2._id } });

  await Publisher.updateOne(
    { _id: publisher1._id },
    { $push: { books: book1._id, books: book2._id } }
  );

  await Publisher.updateOne({ _id: publisher2._id }, { $push: { books: book1._id } });

  // 深度 populate
  const options = {
    path: 'author',
    populate: {
      path: 'books',
      populate: {
        path: 'publishers',
      },
    },
  };

  const books = await Book.find().populate(options);
  console.log(JSON.stringify(books, null, 2));

  // 关闭连接
  mongoose.disconnect();
})();

总结

在本文中,我们介绍了如何使用 Mongoose 实现深度 populate。我们通过定义数据模型,创建数据,以及使用 Mongoose 提供的 populate 方法实现了深度获取相关联数据的功能。对于需要大量嵌套查询的数据场景,通过 Mongoose 提供的深度 populate 功能,可以大大提高查询效率。希望本文对你学习和使用 Mongoose 有所帮助。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65b0eca3add4f0e0ffa43c17