时间戳

¥Timestamps

Mongoose 结构支持 timestamps 选项。如果你设置 timestamps: true,Mongoose 会将两个 Date 类型的属性添加到你的结构中:

¥Mongoose schemas support a timestamps option. If you set timestamps: true, Mongoose will add two properties of type Date to your schema:

  1. createdAt:代表该文档创建时间的日期

    ¥createdAt: a date representing when this document was created

  2. updatedAt:表示本文档上次更新时间的日期

    ¥updatedAt: a date representing when this document was last updated

然后,Mongoose 将在首次插入文档时设置 createdAt,并在你使用 save()updateOne()updateMany()findOneAndUpdate()update()replaceOne()bulkWrite() 更新文档时更新 updatedAt

¥Mongoose will then set createdAt when the document is first inserted, and update updatedAt whenever you update the document using save(), updateOne(), updateMany(), findOneAndUpdate(), update(), replaceOne(), or bulkWrite().

const userSchema = new Schema({ name: String }, { timestamps: true });
const User = mongoose.model('User', userSchema);

let doc = await User.create({ name: 'test' });

console.log(doc.createdAt); // 2022-02-26T16:37:48.244Z
console.log(doc.updatedAt); // 2022-02-26T16:37:48.244Z

doc.name = 'test2';
await doc.save();
console.log(doc.createdAt); // 2022-02-26T16:37:48.244Z
console.log(doc.updatedAt); // 2022-02-26T16:37:48.307Z

doc = await User.findOneAndUpdate({ _id: doc._id }, { name: 'test3' }, { new: true });
console.log(doc.createdAt); // 2022-02-26T16:37:48.244Z
console.log(doc.updatedAt); // 2022-02-26T16:37:48.366Z

createdAt 属性是不可变的,默认情况下,Mongoose 会覆盖任何用户指定的对 updatedAt 的更新。

¥The createdAt property is immutable, and Mongoose overwrites any user-specified updates to updatedAt by default.

let doc = await User.create({ name: 'test' });

console.log(doc.createdAt); // 2022-02-26T17:08:13.930Z
console.log(doc.updatedAt); // 2022-02-26T17:08:13.930Z

doc.name = 'test2';
doc.createdAt = new Date(0);
doc.updatedAt = new Date(0);
await doc.save();

// Mongoose blocked changing `createdAt` and set its own `updatedAt`, ignoring
// the attempt to manually set them.
console.log(doc.createdAt); // 2022-02-26T17:08:13.930Z
console.log(doc.updatedAt); // 2022-02-26T17:08:13.991Z

// Mongoose also blocks changing `createdAt` and sets its own `updatedAt`
// on `findOneAndUpdate()`, `updateMany()`, and other query operations
// **except** `replaceOne()` and `findOneAndReplace()`.
doc = await User.findOneAndUpdate(
  { _id: doc._id },
  { name: 'test3', createdAt: new Date(0), updatedAt: new Date(0) },
  { new: true }
);
console.log(doc.createdAt); // 2022-02-26T17:08:13.930Z
console.log(doc.updatedAt); // 2022-02-26T17:08:14.008Z

请记住,replaceOne()findOneAndReplace() 会覆盖所有非 _id 属性,包括 createdAt 等不可变属性。调用 replaceOne()findOneAndReplace() 将更新 createdAt 时间戳,如下所示。

¥Keep in mind that replaceOne() and findOneAndReplace() overwrite all non-_id properties, including immutable properties like createdAt. Calling replaceOne() or findOneAndReplace() will update the createdAt timestamp as shown below.

// `findOneAndReplace()` and `replaceOne()` without timestamps specified in `replacement`
// sets `createdAt` and `updatedAt` to current time.
doc = await User.findOneAndReplace(
  { _id: doc._id },
  { name: 'test3' },
  { new: true }
);
console.log(doc.createdAt); // 2022-02-26T17:08:14.008Z
console.log(doc.updatedAt); // 2022-02-26T17:08:14.008Z

// `findOneAndReplace()` and `replaceOne()` with timestamps specified in `replacement`
// sets `createdAt` and `updatedAt` to the values in `replacement`.
doc = await User.findOneAndReplace(
  { _id: doc._id },
  {
    name: 'test3',
    createdAt: new Date('2022-06-01'),
    updatedAt: new Date('2022-06-01')
  },
  { new: true }
);
console.log(doc.createdAt); // 2022-06-01T00:00:00.000Z
console.log(doc.updatedAt); // 2022-06-01T00:00:00.000Z

备用属性名称

¥Alternate Property Names

出于这些文档的目的,我们将始终引用 createdAtupdatedAt。但你可以覆盖这些属性名称,如下所示。

¥For the purposes of these docs, we'll always refer to createdAt and updatedAt. But you can overwrite these property names as shown below.

const userSchema = new Schema({ name: String }, {
  timestamps: {
    createdAt: 'created_at', // Use `created_at` to store the created date
    updatedAt: 'updated_at' // and `updated_at` to store the last updated date
  }
});

禁用时间戳

¥Disabling Timestamps

save()updateOne()updateMany()findOneAndUpdate()update()replaceOne()bulkWrite() 均支持 timestamps 选项。设置 timestamps: false 以跳过为该特定操作设置时间戳。

¥save(), updateOne(), updateMany(), findOneAndUpdate(), update(), replaceOne(), and bulkWrite() all support a timestamps option. Set timestamps: false to skip setting timestamps for that particular operation.

let doc = await User.create({ name: 'test' });

console.log(doc.createdAt); // 2022-02-26T23:28:54.264Z
console.log(doc.updatedAt); // 2022-02-26T23:28:54.264Z

doc.name = 'test2';

// Setting `timestamps: false` tells Mongoose to skip updating `updatedAt` on this `save()`
await doc.save({ timestamps: false });
console.log(doc.updatedAt); // 2022-02-26T23:28:54.264Z

// Similarly, setting `timestamps: false` on a query tells Mongoose to skip updating
// `updatedAt`.
doc = await User.findOneAndUpdate({ _id: doc._id }, { name: 'test3' }, {
  new: true,
  timestamps: false
});
console.log(doc.updatedAt); // 2022-02-26T23:28:54.264Z

// Below is how you can disable timestamps on a `bulkWrite()`
await User.bulkWrite([{
  updateOne: {
    filter: { _id: doc._id },
    update: { name: 'test4' },
    timestamps: false
  }
}]);
doc = await User.findOne({ _id: doc._id });
console.log(doc.updatedAt); // 2022-02-26T23:28:54.264Z

你还可以将 timestamps 选项设置为对象以分别配置 createdAtupdatedAt。例如,在下面的代码中,Mongoose 在 save() 上设置 createdAt 但跳过 updatedAt

¥You can also set the timestamps option to an object to configure createdAt and updatedAt separately. For example, in the below code, Mongoose sets createdAt on save() but skips updatedAt.

const doc = new User({ name: 'test' });

// Tell Mongoose to set `createdAt`, but skip `updatedAt`.
await doc.save({ timestamps: { createdAt: true, updatedAt: false } });
console.log(doc.createdAt); // 2022-02-26T23:32:12.478Z
console.log(doc.updatedAt); // undefined

禁用时间戳还可以让你自己设置时间戳。例如,假设你需要更正文档的 createdAtupdatedAt 属性。你可以通过自行设置 timestamps: falsecreatedAt 来实现此目的,如下所示。

¥Disabling timestamps also lets you set timestamps yourself. For example, suppose you need to correct a document's createdAt or updatedAt property. You can do that by setting timestamps: false and setting createdAt yourself as shown below.

let doc = await User.create({ name: 'test' });

// To update `updatedAt`, do a `findOneAndUpdate()` with `timestamps: false` and
// `updatedAt` set to the value you want
doc = await User.findOneAndUpdate({ _id: doc._id }, { updatedAt: new Date(0) }, {
  new: true,
  timestamps: false
});
console.log(doc.updatedAt); // 1970-01-01T00:00:00.000Z

// To update `createdAt`, you also need to set `strict: false` because `createdAt`
// is immutable
doc = await User.findOneAndUpdate({ _id: doc._id }, { createdAt: new Date(0) }, {
  new: true,
  timestamps: false,
  strict: false
});
console.log(doc.createdAt); // 1970-01-01T00:00:00.000Z

子文档的时间戳

¥Timestamps on Subdocuments

Mongoose 还支持在子文档上设置时间戳。请记住,子文档的 createdAtupdatedAt 代表子文档的创建或更新时间,而不是顶层文档。覆盖子文档也会覆盖 createdAt

¥Mongoose also supports setting timestamps on subdocuments. Keep in mind that createdAt and updatedAt for subdocuments represent when the subdocument was created or updated, not the top level document. Overwriting a subdocument will also overwrite createdAt.

const roleSchema = new Schema({ value: String }, { timestamps: true });
const userSchema = new Schema({ name: String, roles: [roleSchema] });

const doc = await User.create({ name: 'test', roles: [{ value: 'admin' }] });
console.log(doc.roles[0].createdAt); // 2022-02-27T00:22:53.836Z
console.log(doc.roles[0].updatedAt); // 2022-02-27T00:22:53.836Z

// Overwriting the subdocument also overwrites `createdAt` and `updatedAt`
doc.roles[0] = { value: 'root' };
await doc.save();
console.log(doc.roles[0].createdAt); // 2022-02-27T00:22:53.902Z
console.log(doc.roles[0].updatedAt); // 2022-02-27T00:22:53.902Z

// But updating the subdocument preserves `createdAt` and updates `updatedAt`
doc.roles[0].value = 'admin';
await doc.save();
console.log(doc.roles[0].createdAt); // 2022-02-27T00:22:53.902Z
console.log(doc.roles[0].updatedAt); // 2022-02-27T00:22:53.909Z

引擎盖下

¥Under the Hood

对于带有时间戳的查询,Mongoose 为每个更新查询添加 2 个属性:

¥For queries with timestamps, Mongoose adds 2 properties to each update query:

  1. updatedAt 添加到 $set

    ¥Add updatedAt to $set

  2. createdAt 添加到 $setOnInsert

    ¥Add createdAt to $setOnInsert

例如,如果你运行以下代码:

¥For example, if you run the below code:

mongoose.set('debug', true);

const userSchema = new Schema({
  name: String
}, { timestamps: true });
const User = mongoose.model('User', userSchema);

await User.findOneAndUpdate({}, { name: 'test' });

你将看到 Mongoose 调试结构的以下输出:

¥You'll see the below output from Mongoose debug mode:

Mongoose: users.findOneAndUpdate({}, { '$setOnInsert': { createdAt: new Date("Sun, 27 Feb 2022 00:26:27 GMT") }, '$set': { updatedAt: new Date("Sun, 27 Feb 2022 00:26:27 GMT"), name: 'test' }}, {...})

请注意 $setOnInsert 对应 createdAt$set 对应 updatedAt。仅当新文档为 upserted 时,MongoDB 的 $setOnInsert 运算符 才会应用更新。因此,例如,如果你只想在创建新文档时设置 updatedAt,则可以禁用 updatedAt 时间戳并自行设置,如下所示:

¥Notice the $setOnInsert for createdAt and $set for updatedAt. MongoDB's $setOnInsert operator applies the update only if a new document is upserted. So, for example, if you want to only set updatedAt if a new document is created, you can disable the updatedAt timestamp and set it yourself as shown below:

await User.findOneAndUpdate({}, { $setOnInsert: { updatedAt: new Date() } }, {
  timestamps: { createdAt: true, updatedAt: false }
});