模型

模型 是从 Schema 定义编译而来的奇特构造函数。 模型的一个实例称为 document。 模型负责从底层 MongoDB 数据库创建和读取文档。

编译你的第一个模型

当你在结构上调用 mongoose.model() 时,Mongoose 会为你编译一个模型。

const schema = new mongoose.Schema({ name: String, size: String });
const Tank = mongoose.model('Tank', schema);

第一个参数是模型所属集合的单一名称。 Mongoose 会自动查找模型名称的复数小写版本。 因此,对于上面的示例,模型 Tank 适用于数据库中的 tanks 集合。

注意: .model() 函数复制 schema。 在调用 .model() 之前,请确保你已将所需的所有内容添加到 schema,包括钩子!

构建文档

模型的一个实例称为 document。 创建它们并将其保存到数据库很容易。

const Tank = mongoose.model('Tank', yourSchema);

const small = new Tank({ size: 'small' });
await small.save();

// or

await Tank.create({ size: 'small' });

// or, for inserting large batches of documents
await Tank.insertMany([{ size: 'small' }]);

请注意,在模型使用的连接打开之前,不会创建/删除任何水箱。 每个模型都有一个关联的连接。 当你使用 mongoose.model() 时,你的模型将使用默认的 mongoose 连接。

await mongoose.connect('mongodb://127.0.0.1/gettingstarted');

如果你创建自定义连接,请改用该连接的 model() 函数。

const connection = mongoose.createConnection('mongodb://127.0.0.1:27017/test');
const Tank = connection.model('Tank', yourSchema);

查询

使用 Mongoose 查找文档很容易,它支持 MongoDB 的 rich 查询语法。 可以使用 modelfindfindByIdfindOnewhere 静态函数检索文档。

await Tank.find({ size: 'small' }).where('createdDate').gt(oneYearAgo).exec();

有关如何使用 查询 api 的更多详细信息,请参阅有关 queries 的章节。

删除

模型具有静态 deleteOne()deleteMany() 函数,用于删除与给定 filter 匹配的所有文档。

await Tank.deleteOne({ size: 'large' });

更新

每个 model 都有自己的 update 方法,用于修改数据库中的文档而不将它们返回到你的应用。 有关更多详细信息,请参阅 API 文档。

// Updated at most one doc, `res.nModified` contains the number
// of docs that MongoDB updated
await Tank.updateOne({ size: 'large' }, { name: 'T-90' });

如果你想更新数据库中的单个文档并将其返回到你的应用,请改用 findOneAndUpdate

改变流

改变流 为你提供了一种监听 MongoDB 数据库中所有插入和更新的方法。 请注意,除非你连接到 MongoDB 副本集,否则更改流会执行 not 工作。

async function run() {
  // Create a new mongoose model
  const personSchema = new mongoose.Schema({
    name: String
  });
  const Person = mongoose.model('Person', personSchema);

  // Create a change stream. The 'change' event gets emitted when there's a
  // change in the database
  Person.watch().
    on('change', data => console.log(new Date(), data));

  // Insert a doc, will trigger the change stream handler above
  console.log(new Date(), 'Inserting doc');
  await Person.create({ name: 'Axl Rose' });
}

上述 异步函数 的输出将如下所示。

2018-05-11T15:05:35.467Z 'Inserting doc'
2018-05-11T15:05:35.487Z 'Inserted doc'
2018-05-11T15:05:35.491Z { _id: { _data: ... },
  operationType: 'insert',
  fullDocument: { _id: 5af5b13fe526027666c6bf83, name: 'Axl Rose', __v: 0 },
  ns: { db: 'test', coll: 'Person' },
  documentKey: { _id: 5af5b13fe526027666c6bf83 } }

你可以阅读有关 在此博文中更改 mongoose 中的流 的更多信息。

意见

MongoDB 视图 本质上是只读集合,其中包含使用 aggregations 从其他集合计算得出的数据。 在 Mongoose 中,你应该为每个视图定义一个单独的模型。 你还可以使用 createCollection() 创建视图。

以下示例展示了如何在 User 模型上创建新的 RedactedUser 视图,以隐藏潜在的敏感信息,例如名称和电子邮件。

// Make sure to disable `autoCreate` and `autoIndex` for Views,
// because you want to create the collection manually.
const userSchema = new Schema({
  name: String,
  email: String,
  roles: [String]
}, { autoCreate: false, autoIndex: false });
const User = mongoose.model('User', userSchema);

const RedactedUser = mongoose.model('RedactedUser', userSchema);

// First, create the User model's underlying collection...
await User.createCollection();
// Then create the `RedactedUser` model's underlying collection
// as a View.
await RedactedUser.createCollection({
  viewOn: 'users', // Set `viewOn` to the collection name, **not** model name.
  pipeline: [
    {
      $set: {
        name: { $concat: [{ $substr: ['$name', 0, 3] }, '...'] },
        email: { $concat: [{ $substr: ['$email', 0, 3] }, '...'] }
      }
    }
  ]
});

await User.create([
  { name: 'John Smith', email: 'john.smith@gmail.com', roles: ['user'] },
  { name: 'Bill James', email: 'bill@acme.co', roles: ['user', 'admin'] }
]);

// [{ _id: ..., name: 'Bil...', email: 'bil...', roles: ['user', 'admin'] }]
console.log(await RedactedUser.find({ roles: 'admin' }));

请注意,Mongoose 目前强制要求视图是只读的。 如果你尝试从视图中 save() 文档,你将从 MongoDB 服务器收到错误。

还有更多

API 文档 涵盖了许多可用的附加方法,例如 countmapReduceaggregate 等。

下一步

现在我们已经介绍了 Models,让我们来看看 文档